大家好,我是王鸽,今天这篇文章主要是讲解Linux设备文件节点是怎么生成?调用的函数是怎么样的?在Linux系统下,设备文件是种特殊的文件类型,其存在的主要意义是沟通用户空间程序和内核空间驱动程序。换句话说,用户空间的应用程序要想使用驱动程序提供的服务,需要经过设备文件来达成。
当然,如果你的驱动程序只是为内核中的其他模块提供服务,则没有必要生成对应的设备文件。
按照通用的规则,Linux系统所有的设备文件都位于/dev目录下。/dev目录在Linux系统中算是一个比较特殊的目录,在Linux系统早期还不支持动态生成设备节点时,/dev目录就是挂载的根文件系统下的/dev,对这个目录下所有文件的操作使用的是根文件系统提供的接口。比如,如果 Linux系统挂载的根文件系统是ext3,那么对/dev目录下所有目录/文件的操作都将使用ext3 文件系统的接口。
随着后来Linux内核的演进,开始支持动态设备节点的生成”,使得系统在启动过程中会自动生成各个设备节点,这就使得/dev目录不必要作为一个非易失的文件系统的形式存在。因此,当前的Linux内核在挂载完根文件系统之后,会在这个根文件系统的/dev目录上重新挂载一个新的文件系统devtmpfs,后者是个基于系统RAM的文件系统实现。当然,对动态设备节点生成的支持并不意味着一定要将根文件系统中的/dev 目录重新挂载到一个新的文件系统上,事实上动态生成设备节点技术的重点并不在文件系统上面。
目前先假定设备节点是通过 Linux 系统下的mknod 命令静态创建。为方便叙述,下面用一个具体的例子来描述设备文件产生过程中的一些关键要素,这个例子的任务很简单:在一个ext3类型的根文件系统中的/dev目录下用mknod命令来创建一个新的设备文件节点demodev,对应的驱动程序使用的设备主设备号为2,次设备号是0,命令形式为
创建字符设备(需 root 权限)
sudo mknod /dev/demodev c
mknod /dev/demodev c 2 0
上述命令成功执行后,将会在/dev目录下生成一个名为demodev的字符设备节点。如果用strace 工具来跟踪一下上面的命令,会发现如下输出(删去了若干不相关部分):
sudo strace mknod /dev/demodev c 2 0
设备文件类型
内核打印:
execve("/bin/mknod", ["mknod", "/dev/demodev", "c", "2", "0"], ...) = 0...mknod("/dev/demodev", S_IFCHR|0666, makedev(2, 0)) = -1 EPERM (Operation not permitted)...write(2, "mknod: /dev/demodev: Operation not permitted\n", ...) = 45exit_group(1)
cat /proc/devices
可见 Linux 下的 mknod 命令最终是通过调用 mknod 函数来实现的,调用时的重要参数有两个,一是设备文件名("/dev/demodev"),二是设备号(makedev(2,0))。设备文件名主要在用户空间使用(比如用户空间程序调用open函数时),而内核空间则使用inode 来表示相应的文件。本书只关注内核空间的操作,对于前面的mknod命令,它将通过系统调用sys_mknod进入内核空间,这个系统调用的原型是:
asmlinkage long sys_mknod(const char __user *filename, umode_t mode,unsigned dev);
注意: sys_mknod 的最后一个参数 dev,它是由用户空间的 mknod 命令构造出的设备号。sys_mknod 系统调用将通过/dev 目录上挂载的文件系统接口来为/dev/demodev 生成一个新的inode,设备号将被记录到这个新的inode 对象上。
展示了通过ext3 文件系统在/dev目录下生成一个新的设备节点/dev/demodev 的主要流程。
sys_mknod 首先在根文件系统ext3的根目录“/”下寻找 dev 目录所对应的inode,图中对应的inode 编号为168,ext3 文件系统的实现会通过某种映射机制,通过inode 编号最终得到该inode在内存中的实际地址(图中由标号1的线段表示)。接下来会通过dev的inode结构中的i_op成员指针所指向的ext3_dir_inode_operations(这是个 struct inode_operations类型的指针),来调用该对象中的mknod方法,这将导致ext3mknod 函数被调用。ext3 mknod 函数的主要作用是生成一个新的inode(用来在内核空间表示 demodev 设备文件节点,demodev设备节点文件与新生成的inode 之间的关联在上图中由标号5的线段表示)。在 ext3 mknod 中会调用一个和设备驱动程序关系密切的init_special_inode 函数,其定义如下:这个函数最主要的功能便是为新生成的inode 初始化其中的i_fop和i_rdev成员。设备文件节点inode 中的i_rdev成员用来表示该inode 所对应设备的设备号,通过参数 rdev 为其赋值。设备号在由sysmknod发起的整个内核调用链中进行传递,最早来自于用户空间的mknod 命令行参数。i_fop成员的初始化根据是字符设备还是块设备而有不同的赋值。对于字符设备,fop指向def_chr_fops,后者主要定义了一个 open 操作:/* * 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,};
相对于字符设备,块设备的def_blk_fops的定义则要有点复杂:
字符设备 inode 中的i_fop 指向 def_chr_fops。
谢谢阅读收藏点赞!