资源描述
,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,信息检索,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,单击此处编辑母版标题样式,信息检索,*,字符设备驱动,第,5,章 字符设备驱动,计算机科学技术系,李伟民,20,12,年,8,月,字符设备驱动,提纲,Linux,字符设备驱动结构,-Linux,设备号管理,-,一些重要的数据结构,globalmem,设备驱动,-globalmem,驱动编写方法和分析,-globalmem,设备驱动在用户空间的验证,字符设备驱动,引言,Linux,提供,3,种基本的设备驱动接口,包括字符,块设备和网络设备驱动,其中字符设备是应用最广,和用户应用程序联系最直接的驱动。,比起块设备或者网络驱动程序更加易于理解。,字符设备驱动,字符设备开发的基本步骤,字符设备的特点,像字节流一样来存取的设备,(,如同文件,),通过,/dev,下的文件系统结点来访问,通常至少需要实现,open,close,read,和,write,等系统调用,只能顺序访问的数据通道,不能前后移动访问指针。特例,:,比如,framebuffer,设备就是这样的设备,应用程序可以使用,mmap,或,lseek,访问图像的各个区域。,字符设备驱动,字符设备开发的基本步骤,确定主设备号和次设备号,实现字符驱动程序,实现,file_operations,结构体,实现初始化函数,注册字符设备,实现销毁函数,释放字符设备,创建设备文件节点,字符设备驱动,主设备号与次设备号,主设备号与次设备号,主设备号:,主设备号是内核识别一个设备的标识。整数,(,占,12bits),,范围从,0,到,4095,,通常使用,1,到,255,。可以通过,/proc/devices,文件来查看驱动系统设备的主设备号。,次设备号:,次设备号由内核使用,用于正确确定设备文件所指的设备,驱动程序遍历设备时,每发现一个它能驱动的设备,就创建一个设备对象,并为其分配一个次设备号以区分不同的设备。这样当应用程序访问设备节点时驱动程序就可以根据次设备号知道它所访问的设备了。整数,(,占,20bits),,范围从,0,到,1048575,,一般使用,0,到,255,。,字符设备驱动,主设备号与次设备号,ls-l/dev,crw-r-1 root root 1,1 Jan 1 00:00 mem,crw-r-1 root root 1,2 Jan 1 00:00 kmem,crw-rw-rw-1 root root 1,3 Jan 1 00:00 null,crw-r-1 root root 1,4 Jan 1 00:00 port,crw-rw-rw-1 root root 1,5 Jan 1 00:00 zero,crw-rw-rw-1 root root 1,7 Jan 1 00:00 full,crw-r-r-1 root root 1,8 Jan 1 00:00 random,crw-r-r-1 root root 1,9 Jan 1 00:00 urandom,crw-rw-rw-1 root root 5,0 Jan 1 00:00 tty,crw-1 root root 5,1 Jan 1 00:00 console,crw-rw-rw-1 root root 5,2 Jan 1 00:00 ptmx,drwxr-xr-x 1 root root 0 Jan 1 00:00 pty,drwxr-xr-x 2 root root 0 Jan 1 00:00 pts,drwxr-xr-x 1 root root 0 Jan 1 00:00 rd,drwxr-xr-x 1 root root 0 Jan 1 00:00 mtd,drwxr-xr-x 1 root root 0 Jan 1 00:00 mtdblock,crw-1 root root 4,64 Jan 1 00:15 ttyS0,crw-1 root root 4,65 Jan 1 00:00 ttyS1,crw-1 root root 4,66 Jan 1 00:00 ttyS2,crw-1 root root 4,67 Jan 1 00:00 ttyS3,crw-1 root root 4,68 Jan 1 00:00 ttyS4,drwxr-xr-x 1 root root 0 Jan 1 00:00 misc,c:,字符设备,b:,块设备,主设备号,次设备号,字符设备驱动,设备编号的内部表达,dev_t,类型,(32,位,),:,用来保存设备编号,(,包括主设备号,(12,位,),和次设备号,(20,位,),。,从,dev_t,获得主设备号和次设备号,:,MAJOR(dev_t,);,MINOR(dev_t,),。,将主设备号和次设备号转换成,dev_t,类型,:,MKDEV(int,major,,,int,minor),字符设备驱动,主设备号与次设备号,分配主设备号,手工分配:,找一个内核没有使用过的主设备号来使用,动态分配:,我们通常不知道设备将要使用哪些主设备号,#include,int register_chrdev_region(dev_t first,unsigned int count,char*name),;,要分配的设备编号范围的起始值,次设备号经常为,0,所请求的连续设备编号的个数,和该编号范围关联的设备名称,它将出现在,/proc/devices,和,sysfs,中,#include,int alloc_chrdev_resion(dev_t*dev,,,unsigned int firstminor,,,unsigned int count,,,char*name),;,输出的设备号,要使用的被请求的第一个次设备号,字符设备驱动,主设备号与次设备号,释放设备号,void unregister_chrdev_region(dev_t first,unsigned int count);,通常在模块的清除函数中调用。,字符设备驱动,Linux,字符设备驱动结构,cdev,结构体,Linux2.6,内核中,使用,cdev,结构体描述一个字符设备,struct cdev,struct kobject kobj;/*,内嵌的,kobject,对象*,/,struct module*owner;/*,所属模块*,/,struct file_operations*ops;/*,文件操作结构体*,/,struct list_head list;,dev_t dev;/*,设备号*,/,unsigned int count;,;,字符设备驱动,Linux,字符设备驱动结构,操作,cdev,的函数,void cdev_init(struct cdev*,struc t file_operations*);,struct cdev*cdev_alloc(void);,int cdev_add(st ruct cdev*,dev_t,unsigned);,void cdev_del(struct cdev*);,用于初始化,cdev,的成员,并建立,cdev,和,file_operations,之间的连接,函数用于动态申请一个,cdev,内存,分别向系统删除一个,cdev,,完成字符设备的注销,通常在模块的卸载函数中调用,分别向系统添加一个,cdev,,完成字符设备的注册,通常在模块加载函数中调用,字符设备驱动,Linux,字符设备驱动结构,file_operations,结构体,字符驱动和内核的接口,在,include/linux/fs.h,中定义,它定义了维系在设备驱动上的操作函数。,在进程,PCB,中,每个打开文件,包括设备文件,(,内部用一个,struct file,结构来代表,稍后我们会看到,),与它自身的函数集合相关连,它通过包含一个称为,f_op,的指向一个,file_operations,结构成员指针建立关系,字符驱动只要实现一个,file_operations,结构体,并注册到内核中,内核就有了操作此设备的能力,用户通过系统调用,open,read,write,等在这个结构中有相应的方法对应,字符设备驱动,Linux,字符设备驱动结构,file_operations,结构体的主要成员,struct module*owner,:,它是一个指向拥有这个结构的模块的指针,它被简单初始化为,THIS_MODULE,一个在,中定义的宏,。,loff_t(*llseek)(struct file*,loff_t,int),:,llseek,方法用作改变文件中的当前读,/,写位置,并且新位置作为,(,正的,),返回值。,ssize_t(*read)(struct file*,char _user*,size_t,loff_t*),:,用来从设备中获取数据。,ssize_t(*aio_read)(struct kiocb*,char _user*,size_t,loff_t),:,初始化一个异步读,-,可能在函数返回前不结束的读操作,.,字符设备驱动,Linux,字符设备驱动结构,file_operations,结构体的主要成员,ssize_t(*write)(struct file*,const char _user*,size_t,loff_t*):,发送数据给设备,.,如果,NULL,-EINVAL,,如果非负,返回值代表成功写的字节数,。,ssize_t(*aio_write)(struct kiocb*,const char _user*,size_t,loff_t*):,初始化设备上的一个异步写。,int(*readdir)(struct file*,void*,filldir_t),:,对于设备文件这个成员应当为,NULL;,它用来读取目录,并且仅对文件系统有用,unsigned int(*poll)(struct file*,struct poll_table_struct*):,poll,方法是,3,个系统调用的后端,:poll,epoll,和,select,都用作查询对一个或多个文件描述符的读或写是否会阻塞,字符设备驱动,Linux,字符设备驱动结构,file_operations,结构体的主要成员,int(*ioctl)(struct inode*,struct file*,unsigned int,unsigned long),:,ioctl,系统调用提供了发出设备特定命令的方法,(,例如格式化软盘的一个磁道,这不是读也不是写,),。,int(*mmap)(struct file*,struct vm_area_struct*),:,mmap,用来请求将设备内存映射到进程的地址空间,.,如果这个方法是,NULL,mmap,系统调用返回,ENODEV,。,int(*open)(struct inode*,struct file*),:,尽管这常常是对设备文件进行的第一个操作,不要求驱动声明一个对应的方法,.,如果这个项是,NULL,设备打开一直成功,但是你的驱动不会得到通知,.,int(*flush)(struct file*),:,flush,操作在进程关闭它的设备文件描述符的拷贝时调用,它应当执行,(,并且等待,),设备的任何未完成的操作,.,如果,flush,为,NULL,内核简单地忽略用户应用程序的请求,.,字符设备驱动,Linux,字符设备驱动结构,file_operations,结构体的主要成员,int(*release)(struct inode*,struct file*),:,在文件结构被释放时用该操作,。,int(*fsync)(struct file*,struct dentry*,int):,这个方法是,fsync,系统调用的后端,用户调用来刷新任何挂着的数据。,int(*fasync
展开阅读全文