前言
由于字符设备和块设备都良好地体现了“一切都是文件”的设计思想,掌握Linux文件系统、设备文件系统的知识就显得相当重要了。
- 首先,驱动最终通过与文件操作相关的系统调用或C库函数(本质也基于系统调用)被访问,而设备驱动的结构最终也是为了迎合提供给应用程序员的API。
- 其次,驱动工程师在设备驱动中不可避免地会与设备文件系统打交道,包括从Linux 2.4内核的devfs文件系统到Linux 2.6以后的udev。
Linux文件系统与设备驱动
图所示为Linux中虚拟文件系统、磁盘/Flash文件系统及一般的设备文件与设备驱动程序之间的关系。
应用程序和VFS之间的接口是系统调用,
而VFS与文件系统以及设备文件之间的接口是file_operations结构体成员函数,
这个结构体包含对文件进行打开、关闭、读写、控制的一系列成员函数,关系如图5.2所示。
-
字符设备
由于字符设备的上层没有类似于磁盘的ext2等文件系统,所以字符设备的file_operations成员函数就直接由设备驱动提供了,在稍后的第6章,将会看到file_operations正是字符设备驱动的核心。 -
块设备
块设备有两种访问方法,- 一种方法是不通过文件系统直接访问裸设备,在Linux内核实现了统一的def_blk_fops这一file_operations,它的源代码位于fs/block_dev.c,所以当我们运行类似于“dd if=/dev/sdb1of=sdb1.img”的命令把整个/dev/sdb1裸分区复制到sdb1.img的时候,内核走的是def_blk_fops这个file_operations;
- 另外一种方法是通过文件系统来访问块设备,file_operations的实现则位于文件系统内,文件系统会把针对文件的读写转换为针对块设备原始扇区的读写。ext2、fat、Btrfs等文件系统中会实现针对VFS的file_operations成员函数,设备驱动层将看不到file_operations的存在。
在设备驱动程序的设计中,一般而言,会关心file和inode这两个结构体。
1.file结构体
file结构体代表一个打开的文件,系统中每个打开的文件在内核空间都有一个关联的struct file。
它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。在内核和驱动源代码中,struct file的指针通常被命名为file或filp(即file pointer)。代码清单5.3给出了文件结构体的定义。
1 struct file {
2 union {
3 struct llist_node fu_llist;
4 struct rcu_head fu_rcuhead;
5 } f_u;
6 struct path f_path;
7 #define f_dentry f_path.dentry
8 struct inode *f_inode; /* cached value */
9 const struct file_operations*f_op; /* 和文件关联的操作*/
10
11 /*
12 * Protects f_ep_links, f_flags.
13 * Must not be taken from IRQ context.
14 */
15 spinlock_t f_lock;
16 atomic_long_t f_count;
17 unsigned int f_flags; /*文件标志,如O_RDONLY、O_NONBLOCK、O_SYNC*/
18 fmode_t f_mode; /*文件读/写模式,FMODE_READ和FMODE_WRITE*/
19 struct mutex f_pos_lock;
20 loff_t f_pos; /* 当前读写位置 */
21 struct fown_struct f_owner;
22 const struct cred *f_cred;
23 struct file_ra_statef_ra;
24
25 u64 f_version;
26 #ifdef CONfiG_SECURITY
27 void *f_security;
28 #endif
29 /* needed for tty driver, and maybe others */
30 void *private_data; /*文件私有数据*/
31
32 #ifdef CONfiG_EPOLL
33 /* Used by fs/eventpoll.c to link all the hooks to this file */
34 struct list_head f_ep_links;
35 struct list_head f_tfile_llink;
36 #endif /* #ifdef CONfiG_EPOLL */
37 struct address_space*f_mapping;
38 } __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
- 文件读/写模式mode、
- 标志f_flags都是设备驱动关心的内容,
- 而私有数据指针private_data在设备驱动中被广泛应用,大多被指向设备驱动自定义以用于描述设备的结构体。
下面的代码可用于判断以阻塞还是非阻塞方式打开设备文件:
if (file->f_flags & O_NONBLOCK) /* 非阻塞 */
pr_debug("open: non-blocking\n");
else /* 阻塞 */
pr_debug("open: blocking\n");
2.inode结构体
VFS inode包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。
它是Linux管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁,inode结构体的定义如代码清单5.4所示。
1 struct inode {
2 ...
3 umode_t i_mode; /* inode的权限 */
4 uid_t i_uid; /* inode拥有者的id */
5 gid_t i_gid; /* inode所属的群组id */
6 dev_t i_rdev; /* 若是设备文件,此字段将记录设备的设备号 */
7 loff_t i_size; /* inode所代表的文件大小 */
8
9 struct timespec i_atime; /* inode最近一次的存取时间 */
10 struct timespec i_mtime; /* inode最近一次的修改时间 */
11 struct timespec i_ctime; /* inode的产生时间 */
12
13 unsigned int i_blkbits;
14 blkcnt_t i_blocks; /* inode所使用的block数,一个block为512 字节 */
15 union {
16 struct pipe_inode_info *i_pipe;
17 struct block_device *i_bdev;
18 /* 若是块设备,为其对应的block_device结构体指针 */
19 struct cdev *i_cdev; /* 若是字符设备,为其对应的cdev结构体指针 */
20 }
21 ...
22 };
- 对于表示设备文件的inode结构,
- i_rdev字段包含设备编号。
- Linux内核设备编号分为主设备编号和次设备编号,前者为dev_t的高12位,后者为dev_t的低20位。
下列操作用于从一个inode中获得主设备号和次设备号:
unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);
查看/proc/devices文件可以获知系统中注册的设备,第1列为主设备号,第2列为设备名,如:
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
21 sg
29 fb
128 ptm
136 pts
171 ieee1394
180 usb
189 usb_device
Block devices:
1 ramdisk
2 fd
8 sd
9 md
22 ide1
...
查看/dev目录可以获知系统中包含的设备文件,日期的前两列给出了对应设备的主设备号和次设备号:
crw-rw---- 1 root uucp 4, 64 Jan 30 2003 /dev/ttyS0
brw-rw---- 1 root disk 8, 0 Jan 30 2003 /dev/sda
- 主设备号是与驱动对应的概念,同一类设备一般使用相同的主设备号,不同类的设备一般使用不同的主设备号(但是也不排除在同一主设备号下包含有一定差异的设备)。
- 因为同一驱动可支持多个同类设备,因此用次设备号来描述使用该驱动的设备的序号,序号一般从0开始。
内核Documents目录下的devices.txt文件描述了Linux设备号的分配情况,它由LANANA(the Linux Assigned Names and Numbers authority,网址为http://www.lanana.org/)组织维护,Torben Mathiasen([email protected])是其中的主要维护者。
内容来自:Linux设备驱动开发详解