




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、1第第3 章章 字符设备驱动程序字符设备驱动程序scull的设计的设计 主设备号与次设备号主设备号与次设备号 一些重要的数据结构一些重要的数据结构 字符设备的注册字符设备的注册 open 和和release scull 的内存使用的内存使用read 和和write2scull的设计的设计scull0 scull3 由全局且持久的内存区组成,可被多次打开,关闭后由全局且持久的内存区组成,可被多次打开,关闭后再打开,仍能保持原数据再打开,仍能保持原数据scullpipe0 to scullpipe3 FIFO(先进先出)设备,类似管道,可由一个进程读(先进先出)设备,类似管道,可由一个进程读,另一
2、个进程写,另一个进程写Scullsingle、scullpriv 、sculluid、scullwuid与与scull0类似,但在类似,但在open操作方面有些限制操作方面有些限制3第第3 章章 字符设备驱动程序字符设备驱动程序scull的设计的设计 主设备号与次设备号主设备号与次设备号 一些重要的数据结构一些重要的数据结构 字符设备的注册字符设备的注册 open 和和release scull 的内存使用的内存使用read 和和write4主设备号与次设备号主设备号与次设备号$ls l /devcrw-rw-rw- 1 root root crw-rw-rw- 1 root root 1 1
3、, , 3 3 Apr 11 2002 null Apr 11 2002 null crw- 1 root root crw- 1 root root 1010, , 1 1 Apr 11 2002 psaux Apr 11 2002 psaux crw- 1 root root crw- 1 root root 4 4, , 1 1 Oct 28 03:04 tty1 Oct 28 03:04 tty1 crw-rw-rw- 1 root tty crw-rw-rw- 1 root tty 4 4, , 6464 Apr 11 2002 ttys0 Apr 11 2002 ttys0 crw
4、-rw- 1 root uucp crw-rw- 1 root uucp 4 4, , 6565 Apr 11 2002 ttyS1 Apr 11 2002 ttyS1 crw-w- 1 vcsa tty crw-w- 1 vcsa tty 7 7, , 1 1 Apr 11 2002 vcs1 Apr 11 2002 vcs1 crw-w- 1 vcsa tty crw-w- 1 vcsa tty 7 7, , 129129 Apr 11 2002 vcsa1 Apr 11 2002 vcsa1 crw-rw-rw- 1 root root crw-rw-rw- 1 root root 1
5、1, , 5 5 Apr 11 2002 zero Apr 11 2002 zero主设备号通常用于标识设备对应的驱动程序,号相同主设备号通常用于标识设备对应的驱动程序,号相同的设备共用一个驱动程序;的设备共用一个驱动程序;次设备号用于标识驱动程序所服务的具体设备次设备号用于标识驱动程序所服务的具体设备主设备号次设备号5主设备号与次设备号主设备号与次设备号设备编号的内部表达设备编号的内部表达内核中,设备编号数据类型为内核中,设备编号数据类型为dev_t,定义在,定义在 中中操作设备编号的宏操作设备编号的宏 MAJOR(dev_t dev); 由由dev_t数得到主设备号数得到主设备号 MINO
6、R(dev_t dev);由由dev_t数得到次设备号数得到次设备号 MKDEV(int major, int minor);由主、次设备号得到由主、次设备号得到dev_t数数主设备号次设备号12位20位dev_t6主设备号与次设备号主设备号与次设备号静态分配设备编号静态分配设备编号int register_chrdev_region(dev_t first, unsigned int int register_chrdev_region(dev_t first, unsigned int count, char count, char * *name);name); firstfirst:主
7、设备号范围起始值,次设备号通常为:主设备号范围起始值,次设备号通常为0 0 countcount:连续设备号的个数:连续设备号的个数 namename:设备编号范围关联的设备名称,将出现在:设备编号范围关联的设备名称,将出现在/proc/devices/proc/devices和和/sysfs/sysfs中中 成功返回成功返回0 0,失败返回负数,失败返回负数静态分配需要预先知道可使用的设备号,静态分配需要预先知道可使用的设备号,如何知道?如何知道? 内核源代码树的内核源代码树的Documenttation/devices.text可查到可查到尚有哪些号可用尚有哪些号可用7主设备号与次设备号主
8、设备号与次设备号动态分配设备编号动态分配设备编号int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name); dev:调用成功后保存分配到的设备编号范围的第一个:调用成功后保存分配到的设备编号范围的第一个数数 firstminor:第一个次设备号,通常用是:第一个次设备号,通常用是0 count与与name与静态分配时相同与静态分配时相同释放设备编号释放设备编号void unregister_chrdev_region(dev_t first, unsigned int
9、 count);8主设备号与次设备号主设备号与次设备号scull设备号分配设备号分配if (scull_major) dev = MKDEV(scull_major, scull_minor); result = register_chrdev_region(dev, scull_nr_devs, scull); else result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, scull); scull_major = MAJOR(dev); if (result 0) if (result 0) printk(p
10、rintk(KERN_WARNINGKERN_WARNING scull: cant get major %dn, scull_major); scull: cant get major %dn, scull_major); return result; return result; scull_major为全局变量,初始化值为为全局变量,初始化值为SCULL_MAJOR,定义在,定义在scull.h中,中, 设为设为0时为动态分配(默认),否则为静态分配。时为动态分配(默认),否则为静态分配。9主设备号与次设备号主设备号与次设备号scull_load静态分配的问题是:若驱动程序仅自己使用,选
11、择一静态分配的问题是:若驱动程序仅自己使用,选择一个未用的号来用没什么问题,但若驱动程序被广泛使个未用的号来用没什么问题,但若驱动程序被广泛使用,则可能造成冲突用,则可能造成冲突动态分配的问题是:由于分配的主设备号不能始终一动态分配的问题是:由于分配的主设备号不能始终一至,所以无法预先创建设备节点至,所以无法预先创建设备节点一旦分配了设备号,就可从一旦分配了设备号,就可从/proc/devices中读到,因中读到,因此可写一脚本代替此可写一脚本代替insmod,在载入模块后读出设备号,在载入模块后读出设备号并创建节点。即用一脚本完成加载模块、读出主设备并创建节点。即用一脚本完成加载模块、读出主
12、设备号及创建设备节点等操作。号及创建设备节点等操作。scull设备的这一脚本叫设备的这一脚本叫scull_load10主设备号与次设备号主设备号与次设备号 /proc/devices 文件如下文件如下:Character devices: 1 mem 2 pty 3 ttyp 4 ttyS 6 lp 7 vcs 10 misc 13 input 14 sound 21 sg180 usb Block devices:2 fd 8 sd 11 sr 65 sd66 sd11主设备号与次设备号主设备号与次设备号/*Scull_load*/#!/bin/sh#!/bin/shmodule=scull
13、 module=scull device=scull device=scull mode=664 mode=664 /sbin/insmod ./$module.ko $/sbin/insmod ./$module.ko $* * | exit 1 | exit 1 rm -f /dev/$device0-3rm -f /dev/$device0-3major=$(awk $2= =$module print $1 /proc/devices) major=$(awk $2= =$module print $1 /proc/devices) mknod /dev/$device0 c $maj
14、or 0mknod /dev/$device0 c $major 0mknod /dev/$device1 c $major 1 mknod /dev/$device1 c $major 1 mknod /dev/$device2 c $major 2mknod /dev/$device2 c $major 2mknod /dev/$device3 c $major 3 mknod /dev/$device3 c $major 3 group=staff“group=staff“grep -q staff: /etc/group | group=wheel“grep -q staff: /et
15、c/group | group=wheel“chgrp $group /dev/$device0-3 chgrp $group /dev/$device0-3 chmod $mode /dev/$device0-3chmod $mode /dev/$device0-3#awk 条件类型条件类型1 动作动作1条件类型条件类型2动作动作2filename#grep参数参数 搜索字符串搜索字符串filename12主设备号与次设备号主设备号与次设备号也可编写一个也可编写一个init脚本放在脚本放在/etc/init.d下,可在系统初下,可在系统初始化时加载模块、并根据主设备号创建节点始化时加载模块、
16、并根据主设备号创建节点若只涉及单个驱动,因动态设备号的分配并不是真正若只涉及单个驱动,因动态设备号的分配并不是真正随机生成的,故只需在第一次加载时创建节点,以后随机生成的,故只需在第一次加载时创建节点,以后加载时无需再创节点加载时无需再创节点13第第3 章章 字符设备驱动程序字符设备驱动程序scull的设计的设计 主设备号与次设备号主设备号与次设备号 一些重要的数据结构一些重要的数据结构 字符设备的注册字符设备的注册 open 和和release scull 的内存使用的内存使用read 和和write14一些重要的数据结构一些重要的数据结构大部分的驱动程序操作涉及到大部分的驱动程序操作涉及到
17、3个重要的内核数据结个重要的内核数据结构,分别是:构,分别是:file_operations:文件操作结构,保存文件操作方法:文件操作结构,保存文件操作方法 file:文件结构,对应一个打开的文件:文件结构,对应一个打开的文件inode:节点结构,对应一个文件:节点结构,对应一个文件在编写真正的驱动程序前,需要对这在编写真正的驱动程序前,需要对这3个结构有一个个结构有一个基本的认识。基本的认识。15一些重要的数据结构一些重要的数据结构file_operations,在,在中定义中定义struct file_operations struct module *owner ;loff_t (*ll
18、seek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char _ _user *, size_t, loff_t *); ssize_t (*aio_read)(struct kiocb *, char _ _user *, size_t, loff_t); ssize_t (*write) (struct file *, const char _ _user *, size_t, loff_t *); ssize_t (*aio_write)(struct kiocb *, const char _ _use
19、r *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); 16一些重要的数据结构一些重要的数据结构int (*open
20、) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int); int (*aio_fsync)(struct kiocb *, int); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); s
21、size_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t
22、, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*dir_notify)(struct file *, unsigned long);17一些重要的数据结构一些重要的数据结构struct module *owner第一个第一个 file_operations 成员不是一个操作,是一个指成员不是一个操作,是一个指向拥有这个结构的模
23、块的指针。向拥有这个结构的模块的指针。 这个成员用来在它的这个成员用来在它的操作还在被使用时阻止模块被卸载。操作还在被使用时阻止模块被卸载。 几乎所有时间中几乎所有时间中, 它被初始化为它被初始化为 THIS_MODULE, 一个在一个在 中定义的宏。中定义的宏。loff_t (*llseek) (struct file *, loff_t, int);llseek 方法用于改变文件中的当前读方法用于改变文件中的当前读/写位置写位置, 并且将并且将新位置作为新位置作为(正的正的)返回值。返回值。loff_t 参数是一个参数是一个“long offset”, 并且就算在并且就算在 32 位平台上
24、也至少有位平台上也至少有 64 位宽。位宽。 出错时返回一个负的返回值。出错时返回一个负的返回值。 如果这个函数指针是如果这个函数指针是 NULL, seek 调用会以无法预测的方式修改调用会以无法预测的方式修改 file 结构中结构中的位置计数器的位置计数器( 在在file 结构结构 一节中描述一节中描述)。18一些重要的数据结构一些重要的数据结构ssize_t (*read) (struct file *, char _user *, size_t, loff_t *);用来从设备中获取数据。非负返回值代表成功读取的用来从设备中获取数据。非负返回值代表成功读取的字节数字节数( 返回值是一个
25、返回值是一个 “signed size” 类型类型, 常常是目常常是目标平台本地的整数类型标平台本地的整数类型)。该函数指针为。该函数指针为NULL时将导时将导致调用失败并返回致调用失败并返回 -EINVAL(“Invalid argument”) ssize_t (*aio_read)(struct kiocb *, char _user *, size_t, loff_t);初始化一个异步读初始化一个异步读 - 在函数返回前可能不会结束的读在函数返回前可能不会结束的读操作。操作。 如果这个方法是如果这个方法是 NULL,所有的读操作会由所有的读操作会由 read 代替进行代替进行(同步地同
26、步地)。19一些重要的数据结构一些重要的数据结构ssize_t (*write) (struct file *, const char _user *, size_t, loff_t *);发送数据给设备。发送数据给设备。 非负返回值代表成功发送的字节数非负返回值代表成功发送的字节数.ssize_t (*aio_write)(struct kiocb *, const char _user *, size_t, loff_t *);设备上的一个异步写入方法。设备上的一个异步写入方法。int (*readdir) (struct file *, void *, filldir_t);对于设备文件
27、,这个成员应当为对于设备文件,这个成员应当为 NULL; 它用来读取目它用来读取目录录, 仅对文件系统有用仅对文件系统有用20一些重要的数据结构一些重要的数据结构unsigned int (*poll) (struct file *, struct poll_table_struct *);poll 方法是方法是 poll, epoll 和和 select 3 个系统调用的后端实现,用个系统调用的后端实现,用来查询对一个或多个文件描述符的读或写是否会被阻塞。来查询对一个或多个文件描述符的读或写是否会被阻塞。 poll 应当返回一个位掩码,用于指示非阻塞的读或写是否可能应当返回一个位掩码,用于指
28、示非阻塞的读或写是否可能, 并且并且也会向内核提供将调用进程置于睡眠状态直到也会向内核提供将调用进程置于睡眠状态直到I/O 变为可能时的变为可能时的信息。信息。 如果一个驱动的如果一个驱动的 poll 方法为方法为 NULL, 则设备会被认为可则设备会被认为可读可写,且不会被阻塞。读可写,且不会被阻塞。int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);ioctl 方法提供了操作设备的非读写方法方法提供了操作设备的非读写方法(如格式化软盘如格式化软盘)。 另外另外,内核还能识别一些内核还能识别一些
29、ioctl 命令而不必调用命令而不必调用 fops 表的表的ioctl。 如果如果设备不提供设备不提供 ioctl 方法方法, 对于内核未预先定义的请求,对于内核未预先定义的请求, ioctl系统系统调用将返回错误调用将返回错误(-ENOTTY, 即设备无这样的即设备无这样的ioctl)21一些重要的数据结构一些重要的数据结构int (*mmap) (struct file *, struct vm_area_struct *);mmap 用来请求将设备内存映射到进程的地址空间。用来请求将设备内存映射到进程的地址空间。 如果如果这个方法是这个方法是 NULL, mmap 系统调用返回系统调用返
30、回 -ENODEV。int (*open) (struct inode *, struct file *);通常是对设备文件进行的第一个操作通常是对设备文件进行的第一个操作, 但并不要求驱动一定但并不要求驱动一定要声明一个对应的方法。要声明一个对应的方法。 如果这个项是如果这个项是 NULL, 设备的打设备的打开永远成功开永远成功, 但系统不会通知驱动程序但系统不会通知驱动程序int (*release) (struct inode *, struct file *);在在file结构被释放时调用这个操作。结构被释放时调用这个操作。 如同如同 open, release 可可以为以为 NULL
31、22一些重要的数据结构一些重要的数据结构int (*fsync) (struct file *, struct dentry *, int);这个方法是这个方法是 fsync 系统调用的后端实现系统调用的后端实现, 用户调用它用户调用它来刷新任何待处理的数据。来刷新任何待处理的数据。 如果为如果为 NULL, fsync系统系统调用返回调用返回 -EINVAL。int (*aio_fsync)(struct kiocb *, int);这是这是 fsync 方法的异步版本方法的异步版本int (*fasync) (int, struct file *, int);这个操作用来通知设备其这个操作
32、用来通知设备其 FASYNC 标志发生了改变。标志发生了改变。异步通知是一个高级的主题异步通知是一个高级的主题, 在第在第 6 章中描述。如果驱章中描述。如果驱动不支持异步通知,这个成员可以是动不支持异步通知,这个成员可以是NULL。23一些重要的数据结构一些重要的数据结构int (*lock) (struct file *, int, struct file_lock *);lock 方法用来实现文件加锁方法用来实现文件加锁; 加锁对常规文件是必不加锁对常规文件是必不可少的特性可少的特性, 但是设备驱动几乎从不实现它。但是设备驱动几乎从不实现它。ssize_t (*readv) (struc
33、t file *, const struct iovec *, unsigned long, loff_t *);ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);这些方法用来实现分散这些方法用来实现分散/汇聚型的读写操作。汇聚型的读写操作。 应用程序应用程序有时需要进行涉及多个内存区的单个读或写操作,有时需要进行涉及多个内存区的单个读或写操作, 这这些系统调用允许它们这样做而不必额外增加数据拷贝些系统调用允许它们这样做而不必额外增加数据拷贝操作。操作。 如果这些函数指针为如果这些函
34、数指针为 NULL,就会调用就会调用read 和和 write 方法方法 ( 可能多于一次可能多于一次 )。24一些重要的数据结构一些重要的数据结构ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *);这个方法实现这个方法实现 sendfile 系统调用的读。系统调用的读。 其用最少的拷其用最少的拷贝操作从一个文件描述符搬移数据到另一个。贝操作从一个文件描述符搬移数据到另一个。 例如例如, web 服务器可以利用这个方法将某一个文件的内容发服务器可以利用这个方法将某一个文件的内容发送到网络连接。设备
35、驱动常常使送到网络连接。设备驱动常常使 sendfile 为为 NULL。ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);sendpage 是是 sendfile 的另一半的另一半; 它由内核调用来发它由内核调用来发送数据到对应的文件送数据到对应的文件, 一次一页。一次一页。 设备驱动实际上不设备驱动实际上不实现实现 sendpage。25一些重要的数据结构一些重要的数据结构unsigned long (*get_unmapped_area)(struct file *, unsign
36、ed long, unsigned long, unsigned long, unsigned long);这个方法的目的是在进程的地址空间找一个合适的位这个方法的目的是在进程的地址空间找一个合适的位置置,以便将底层设备中的内存段映射到该位置。以便将底层设备中的内存段映射到该位置。 该任务该任务通常由内存管理代码完成,但该方法的存在可允许驱通常由内存管理代码完成,但该方法的存在可允许驱动程序强制满足特定设备需要的任何对齐需求。大部动程序强制满足特定设备需要的任何对齐需求。大部分驱动可以置这个方法为分驱动可以置这个方法为 NULL。int (*check_flags)(int)这个方法允许模块检
37、查传递给这个方法允许模块检查传递给 fnctl(F_SETFL.) 调用调用的标志的标志26一些重要的数据结构一些重要的数据结构int (*dir_notify)(struct file *, unsigned long);这个方法在应用程序使用这个方法在应用程序使用 fcntl 来请求目录改变通知时来请求目录改变通知时调用。调用。 只对文件系统有用只对文件系统有用;驱动不需要实现驱动不需要实现 dir_notify。27一些重要的数据结构一些重要的数据结构scull 设备驱动只实现最重要的设备方法。 它的 file_operations 结构是如下初始化的:struct file_opera
38、tions scull_fops = struct file_operations scull_fops = .owner = THIS_MODULE, .owner = THIS_MODULE, .llseek = scull_llseek, .llseek = scull_llseek, .read = scull_read, .read = scull_read, .write = scull_write, .write = scull_write, .ioctl = scull_ioctl, .ioctl = scull_ioctl, .open = scull_open, .open
39、 = scull_open, .release = scull_release, .release = scull_release, ; ;28一些重要的数据结构一些重要的数据结构struct file, defined in 文件结构代表一个打开的文件,由内核在文件结构代表一个打开的文件,由内核在 open 时创时创建建, 并传递给在文件上操作的任何函数并传递给在文件上操作的任何函数, 直到最后关直到最后关闭闭在文件的所有实例都关闭后在文件的所有实例都关闭后, 内核释放这个数据结构内核释放这个数据结构在内核源码中在内核源码中, 指向指向struct file 的指针常常称为的指针常常称为 f
40、ile 或者或者 filp(“file pointer”)。 本书用本书用filp表示这个指针表示这个指针,以避免和结构自身混淆。因此,以避免和结构自身混淆。因此, file 指的是结构指的是结构, 而而 filp 是结构指针是结构指针29一些重要的数据结构一些重要的数据结构struct filemode_t f_mode; loff_t f_pos; unsigned int f_flags; struct file_operations *f_op;void *private_data; struct dentry *f_dentry;30一些重要的数据结构一些重要的数据结构mode_t
41、f_mode;文件模式。它通过文件模式。它通过 FMODE_READ 和和FMODE_WRITE位来标示文件是否可读或可写位来标示文件是否可读或可写(或两者或两者都是都是)loff_t f_pos;当前读写位置。当前读写位置。 loff_t 在所有平台下都是在所有平台下都是 64 位位( 在在 gcc 术语里是术语里是 long long )。驱动如果需要知道文件中。驱动如果需要知道文件中的当前位置,可以读这个值,但不应该修改它。的当前位置,可以读这个值,但不应该修改它。read/write会使用它们接收到的最后那个指针参数来会使用它们接收到的最后那个指针参数来更新这一位置更新这一位置, 而不
42、是直接对而不是直接对filp-f_pos进行操作。进行操作。 这一规则的一个例外是这一规则的一个例外是 llseek, 它的目的就是修改文件它的目的就是修改文件位置位置31一些重要的数据结构一些重要的数据结构unsigned int f_flags;这些是文件标志这些是文件标志, 例如例如 O_RDONLY, O_NONBLOCK, 和和 O_SYNC。为了检查用户请求的是否非阻塞操作,。为了检查用户请求的是否非阻塞操作,驱动程序应当检查驱动程序应当检查O_NONBLOCK 标志。而其他标志标志。而其他标志很少用到。注意很少用到。注意, 检查读检查读/写权限应该查看写权限应该查看f_mode
43、,而,而非非 f_flags。 所有的标志在头文件所有的标志在头文件 中中定义。定义。32一些重要的数据结构一些重要的数据结构struct file_operations *f_op;与文件关联的操作。内核在执行与文件关联的操作。内核在执行open操作时对指针赋操作时对指针赋值,以后需要处理这些操作时就读取这个指针。值,以后需要处理这些操作时就读取这个指针。 filp-f_op 中的值决不会为方便引用而保存起来。中的值决不会为方便引用而保存起来。 这意味这意味着你可在需要的时候修改文件关联的操作着你可在需要的时候修改文件关联的操作, 在返回调用在返回调用者之后新方法就会生效。者之后新方法就会生
44、效。 例如,对应于主设备号例如,对应于主设备号 1 (/dev/null, /dev/zero, 等等等等)的的 open 代码根据要打开代码根据要打开的次设备号替代的次设备号替代 filp-f_op 中的操作。中的操作。 这种技巧允许这种技巧允许相同主设备号下的设备实现多种操作行为相同主设备号下的设备实现多种操作行为, 而不会增加而不会增加系统调用的负担。这种替换文件操作的能力在面向对系统调用的负担。这种替换文件操作的能力在面向对象编程中称为象编程中称为方法重载方法重载。33一些重要的数据结构一些重要的数据结构void *private_data;open 系统调用在调用驱动程序的系统调用在
45、调用驱动程序的 open 方法之前将这方法之前将这个指针设置为个指针设置为 NULL。驱动程序可以将这个字段用于。驱动程序可以将这个字段用于任何目的或者忽略它。驱动程序可以用这个成员来指任何目的或者忽略它。驱动程序可以用这个成员来指向已分配的数据向已分配的数据, 但是一定要在内核销毁但是一定要在内核销毁file结构之前结构之前, 在在 release 方法中释放内存。方法中释放内存。 private_data是一个有是一个有用的资源用的资源, 可用于在系统调用间保存状态信息可用于在系统调用间保存状态信息, 本书大本书大部分例子模块都使用它。部分例子模块都使用它。struct dentry *f
46、_dentry;文件对应的目录项文件对应的目录项( dentry )结构。除了用结构。除了用 filp-f_dentry-d_inode 的方式来访问索引节点的方式来访问索引节点 inode 结构之外,设备驱动开发者一般无需要关心结构之外,设备驱动开发者一般无需要关心 dentry结结构构 34一些重要的数据结构一些重要的数据结构struct inode,内核用内核用inode 结构在内部表示文件,结构在内部表示文件, 因此它和因此它和file结构不同,后者表示打开的文件。对于单个文结构不同,后者表示打开的文件。对于单个文件,可能会有许多个表示打开的文件描述符件,可能会有许多个表示打开的文件描
47、述符file结结构构, 但它们都指向单个但它们都指向单个inode 结构。结构。inode 结构包含了大量有关文件的信息。作为一结构包含了大量有关文件的信息。作为一个通用的规则个通用的规则, 这个结构只有这个结构只有 2 个成员对于编写个成员对于编写驱动代码有用驱动代码有用:struct inodedev_t i_rdev; struct cdev *i_cdev; ;35一些重要的数据结构一些重要的数据结构dev_t i_rdev;对于表示设备文件的节点对于表示设备文件的节点inode结构结构, 这个成员包这个成员包含了实际的设备编号含了实际的设备编号struct cdev *i_cdev;
48、struct cdev 是内核的内部结构是内核的内部结构, 代表字符设备代表字符设备;当当inode指向一个字符设备文件时,这个成员包含一指向一个字符设备文件时,这个成员包含一个指向个指向struct cdev结构的指针结构的指针 内核开发者已经增加了内核开发者已经增加了 2 个宏个宏, 可用来从一个可用来从一个 inode 中获取主次编号中获取主次编号:unsigned int iminor(struct inode *inode); unsigned int imajor(struct inode *inode);36第第3 章章 字符设备驱动程序字符设备驱动程序scull的设计的设计 主
49、设备号与次设备号主设备号与次设备号 一些重要的数据结构一些重要的数据结构 字符设备的注册字符设备的注册 open 和和release scull 的内存使用的内存使用read 和和write37字符设备的注册字符设备的注册内核字符设备结构与接口(在内核字符设备结构与接口(在中)中)struct cdev struct kobject kobj;struct module *owner;const struct file_operations *ops;struct list_head list;dev_t dev;unsigned int count;struct cdev *cdev_all
50、oc(void); void cdev_init(struct cdev *, const struct file_operations *);int cdev_add(struct cdev *, dev_t, unsigned);void cdev_del(struct cdev *);38字符设备的注册字符设备的注册分配分配struct cdev 结构(动态):结构(动态):struct cdev *my_cdev = cdev_alloc();my_cdev-ops = &my_fops;初始化初始化struct cdev 结构结构void cdev_init(struct c
51、dev *cdev, struct file_operations *fops);将将struct cdev 结构的信息告诉内核结构的信息告诉内核int cdev_add(struct cdev *dev, dev_t num, unsigned int count); num:该设备对应的第一个设备编号 count:与该设备关联的设备编号的数量39字符设备的注册字符设备的注册cdev_add使用注意事项:使用注意事项:这个调用可能失败。如果它返回一个负的错误码这个调用可能失败。如果它返回一个负的错误码, 则设则设备并没有增加到系统中,故需要检测它的返回值备并没有增加到系统中,故需要检测它的返
52、回值 cdev_add 一返回一返回, 设备就设备就“活活”了,它的操作就会被了,它的操作就会被内核调用。因此,如果你的驱动程序没有完全准备好内核调用。因此,如果你的驱动程序没有完全准备好处理设备上的操作处理设备上的操作, 你不应当调用你不应当调用 cdev_add.要从系统中去除一个字符设备时要从系统中去除一个字符设备时, 调用调用:void cdev_del(struct cdev *dev);40字符设备的注册字符设备的注册Scull设备注册设备注册先定义自己的设备结构先定义自己的设备结构scull_dev,并包含内,并包含内核字符设备结构核字符设备结构cdevstruct scull_
53、dev struct scull_qset *data; /* Pointer to first quantum set */ int quantum; /* the current quantum size */ int qset; /* the current array size */ unsigned long size; /* amount of data stored here */ unsigned int access_key; /* used by sculluid and scullpriv */ struct semaphore sem; /* mutual exclus
54、ion semaphore */ struct cdev cdev; /* Char device structure */;41字符设备的注册字符设备的注册初始化初始化struct cdevstatic void scull_setup_cdev(struct scull_dev *dev, int index) int err, devno = MKDEV(scull_major, scull_minor + index); cdev_init(&dev-cdev, &scull_fops); dev-cdev.owner = THIS_MODULE; dev-cdev.o
55、ps = &scull_fops; err = cdev_add (&dev-cdev, devno, 1); /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE Error %d adding scull%d, err, index);42字符设备的注册字符设备的注册int scull_init_module(void)int result, i;dev_t dev = 0;if (scull_major) dev = MKDEV(scull_major, scull_minor);result = reg
56、ister_chrdev_region(dev, scull_nr_devs, scull); else result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,scull); scull_major = MAJOR(dev);if (result 0) printk(KERN_WARNING scull: cant get major %dn, scull_major);return result;43字符设备的注册字符设备的注册scull_devices = kmalloc(scull_nr_devs * size
57、of(struct scull_dev), GFP_KERNEL);if (!scull_devibces) result = -ENOMEM;goto fail; memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev); for (i = 0; i scull_nr_devs; i+) scull_devicesi.quantum = scull_quantum;scull_devicesi.qset = scull_qset;init_MUTEX(&scull_devicesi.sem);scull_set
58、up_cdev(&scull_devicesi, i); struct scull_dev *scull_devices;#define SCULL_NR_DEVS 4 int scull_nr_devs = SCULL_NR_DEVSstruct scull_dev struct scull_qset *data; int quantum; int qset; unsigned long size; unsigned int access_key; struct semaphore sem; struct cdev cdev; ;44字符设备的注册字符设备的注册return 0; f
59、ail:scull_cleanup_module();return result;module_init(scull_init_module);module_exit(scull_cleanup_module);45字符设备的注册字符设备的注册void scull_cleanup_module(void)int i;dev_t devno = MKDEV(scull_major, scull_minor);if (scull_devices) for (i = 0; i private_data里的数据结构里的数据结构open原型原型 int (*open)(struct inode *ino
60、de, struct file *filp); inode中包含有中包含有cdev结构,但是需要的是结构,但是需要的是scull_devstruct inode dev_t i_rdev; struct cdev *i_cdev; ;49open 和和release利用利用container_of(pointer, container_type, container_field);可由可由cdev结构得到包含它的结构得到包含它的scull_dev结构结构 struct scull_dev *dev; dev = container_of(inode-i_cdev, struct scull_dev, cdev);得到得到scull_dev后,将其保存在后,将其保存
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年精密感光材料合作协议书
- 2025年南京市国内旅游合同(官方范本年版)
- 2025年风电铸件项目建议书
- 2025年湿式静电除尘器合作协议书
- 2025年钻井设备合作协议书
- 2025年节能、高效果蔬保鲜装置项目建议书
- 2025年光学纤维面板系列项目建议书
- 2025年弯曲机粉末冶金制品项目合作计划书
- 营销组织和管理平台设计的报告范例 (快速消费品企业)
- 山体填筑施工方案
- 供电局标准用电手续办理流程(课件)
- 《清水混凝土技术》课件
- 合同自动续签模板
- 架线弧垂计算表(应力弧垂插值计算)
- 水电安装全套技术交底
- 三都县一起少数民族陆氏家族的调查
- Pentacam三维眼前节分析仪在眼科临床中的应用
- 顺式-甘氨酸合铜的制备及成份分析课件
- 刑法分论课件(第三章-危害公共安全罪)
- 【深信服】PT1-adesk认证考试复习题库(含答案)
- 房屋买卖合同个人房屋买卖合同
评论
0/150
提交评论