文件系统关联
设备文件都是由标准函数处理,类似普通文件。设备文件也是通过虚拟文件系统来管理的,和普通文件都是通过完全相同的接口访问的。
inode中设备文件的成员数据
- 虚拟文件系统每个文件都关联到一个inode,用于管理文件的属性。源码如下:
- 唯一地标识与一个设备文件关联的设备,内核在
i_mode
中存储文件类型(面向块,面向字符),而且在i_rdev
中存储主从设备号。主从设备号在内核中合并为一种变量类型为dev
*_t
。 - 在内核开发当中认为必要的时候会进行修改,只应该使用两个函数
imajor
和iminor
来从i_rdev
提取主设备号和从设备号,这两个函数都只需要一个指向inode
实例的指针作为参数。 const struct file_operations* i_fop
是一组函数指针的集合,包括许多文件操作(打开,读取,写入等等),这些由虚拟文件系统使用来处理块设备。内核会根据inode表示块设备还是字符设备,使用i_bdev
或i_cdev
指向更具体的信息。- 实际上,
inode
是VFS
使用的一个现象,用于存放内核在操作文件或目录时所需要的全部信息。索引节点有两种:一种是VFS节点,存在内核中;另一种具体文件系统的索引节点,存在磁盘当中,使用时将其读入内存填充VFS节点,之后对VFS索引节点的任何修改都将写回磁盘和磁盘的索引节点。
标准文件操作
在打开一个设备文件时,各种文件系统的实现基本都会调用init_special_inode
函数,为块设备或字符设备创建一个inode
。具体源码如下:
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev) { inode->i_mode = mode; if (S_ISCHR(mode)) { inode->i_fop = &def_chr_fops; inode->i_rdev = rdev; } else if (S_ISBLK(mode)) { inode->i_fop = &def_blk_fops; inode->i_rdev = rdev; } else if (S_ISFIFO(mode)) inode->i_fop = &pipefifo_fops; else if (S_ISSOCK(mode)) ; /* leave it no_open_fops */ else printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for" " inode %s:%lu\n", mode, inode->i_sb->s_id, inode->i_ino); } EXPORT_SYMBOL(init_special_inode);
- 除了通过
mode
传递进来的设备类型之外,底层文件系统还必须返回主从设备号。代码中会根据设备类型,向inode
提供不同的文件操作。
字符设备标准操作
每个设备文件都需要一组独立、自定义操作。具体源码如下:
/* * Dummy default file-operations: the only thing this does * is contain the open that then fills in the correct operations * depending on the special file... */ const struct file_operations def_chr_fops = { .open = chrdev_open, .llseek = noop_llseek, };
块设备标准操作
与自负设备相比,块设备操作的指针会集中到一个叫做blk_fops
通用的结构体。具体源码如下:
const struct file_operations def_blk_fops = { .open = blkdev_open, .release = blkdev_close, .llseek = block_llseek, .read_iter = blkdev_read_iter, .write_iter = blkdev_write_iter, .iopoll = blkdev_iopoll, .mmap = generic_file_mmap, .fsync = blkdev_fsync, .unlocked_ioctl = block_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_blkdev_ioctl, #endif .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .fallocate = blkdev_fallocate, };
字符设备操作
前面说到过,字符设备是由struct cdev
表示,同时内核维护一个数据库,包括所有活动的cdev
实例。
表示字符设备具体源码如下
打开设备文件
通过chrdev_open打开字符设备的通用函数。具体源码如下:
- 假设表示设备文件的
inode
没有被打开过,根据给出的设备编号,kobject_lookup
查询字符设备的数据库并返回与该驱动程序关联的kobject
实例,返回值可用于获取cdev
实例。
- 获得对应设备的
cdev
实例,内核通过cdev->ops
还可以访问特定于设备的file_operations
。接下来我们就设备的各种数据结构之间的关联关系如下图所示:
读写操作
读写字符设备文件,其实虚拟文件和设备驱动程序代码之间已建立关联。调用标准库的读写操作,向内核发出一些系统调用,最终会调用file_operations
结构体中的相关操作(重点是read和write)。只需要调用内核函数来完成操作,具体源码如下: