引子
遵从"Everything is a file"的思想,在Linux下想要操作设备的话,是需要通过一类叫做设备节点(device node)的特殊文件。设备节点通常位于/dev这个目录下,但是位于其它目录也是可以的。只要翻开一本介绍Linux驱动的书,或者在网上搜索相关的文章,很大概率上会教你在写好驱动代码后,使用mknod这个命令创建设备节点用于测试你的代码。但是作为初学者,肯定会产生一个疑问,Linux系统启动后,/dev目录下就有很多设备,这些设备节点是怎么创建的呢?这时你就会碰到udev或者mdev这样的概念。如果再去看内核代码的话,又会发现好像内核代码也会创建设备节点。
那么这些方式到底有什么区别和联系,各自存在的原因又是什么呢?实际上,随着Linux内核不断发展,创建设备节点的方式也在变化。这就造成了有些信息混乱。个人就此进行了一些整理和总结,不会涉及到任何代码,只是说明不同时期的设计思路和演变原因,以及加上个人的一些揣测。
远古时期的静态设备节点
大概在v2.2及之前,Linux的设备号(device number)分为major和minor。最初,major和minor都只占8比特,并且什么设备对应到哪个设备号都是由驱动代码事先申请好的。当操作设备节点时,内核会根据这个设备节点对应的major进行查表操作,以找到相应的驱动。因为那时需要管理的设备还不是很多,所以这种方式并没有很大的问题。但是随着驱动和设备越来越多,这种方法的缺陷也显现出来了。
- /dev目录下文件太多。因为即使没有相应的设备,也会创建设备节点,存在大量无用节点。此时/dev是位于磁盘上的,占用空间。
- 设备号不够用了。
- 设备号冲突。因为只是通过文档来约束驱动实现者选择major。万一两个不同的驱动使用了相同的major,就会存在问题。
- 根文件系统(root file system)不能是只读的。
不被看好的devfs
为了解决上面提到的这些问题,Richard Gooch在1998年开始开发devfs,并且在2000年将其合并到了内核主干中。devfs的设计思路包括
- 直接由驱动创建和管理相应的设备节点。
- 设备节点文件中不包含设备号,而是直接包含相应的设备操作方法。
- 设备节点的命名采用树形结构。
- 只在检测到实际硬件时,才在/dev目录下创建设备节点。
由于devfs不再使用设备号来索引设备,设备号相关的问题都被解决了。而且设备节点是动态创建的,也解决了/dev目录过大的问题。同时,还提出了一些其它的改进方向,内核可以向用户空间发送设备创建的事件,用户空间可以利用这些事件通知来做一些事情,例如更改设备权限,自动运行脚本来配置复杂设备。
从设计思想上来讲,devfs应该是先进了许多。虽然devfs的部分使用者反馈不错,但是真正使用了devfs的发行版不是特别多,主要是SGI在推动。devfs没有成主流的可能原因有代码质量不高;对于/dev目录结构改变过大;开发者本身不活跃;没有完全实现最初的设计目标。而且不久之后就出现了另外一种解决方案,也就是下面要提到的udev。因此,devfs最终还是被移除出内核了。
用户空间的udev
Udev的开发应该是开始于devfs出现了一段时间之后。Udev的开发者不太认同devfs的做法,认为对于设备节点的命名属于策略(policy),不应该位于内核代码中。因此,希望在用户空间来进行设备节点的命名。随着sysfs将设备信息提供给了用户空间,同时/sbin/hotplug提供了设备添加和移除的通知,有可能在用户空间来解决设备节点命名的问题,从而实现/dev目录的动态管理。因此,udev必须配合sysfs来使用。
内核的devtmpfs
从推广效果来看,udev似乎得到了比devfs更多的支持,并且直接造成了devfs被移除。只是udev也有自己的不足,一个是影响了启动速度,另外一个就是要在用户空间实现udev才能创建设备节点。前面一个问题的原因是,内核在添加和移除设备时都会产生一个uevent,在处理完所有的uevent之后,其它用户态程序几乎不能启动。这两个问题对于嵌入式设备影响都比较大,例如Android就想尽办法不使用udev。
这里有趣的一点是,作为udev的开发者之一,Greg Kroah-Hartman当时反对devfs的重要理由之一就是设备命名不应该存在于内核中。但是devtmpfs做的实际上也是这件事情,Greg Kroah-Hartman却没有异议。对此,另外一个devtmpfs的开发者Kay Sievers给出的解释是,98%的设备命名策略事实上已经位于内核之中,因此不能成为devtmpfs的反对理由。
在devtmpfs产生之初,还是有一些疑虑的。不过最终devtmpfs没有步devfs的后尘。现在内核默认配置是启用devtmpfs的。
个人随想
从以上过程可以看出,Linux对于设备节点的创建方式并不是一成不变的。最开始的mknod/MAKDEV方式,可以看作由用户空间来决定。之后的devfs方式,是由内核代码决定的。而udev方式又变回了用户空间方式来决定。最终现在的devtmpfs加udev则可以看作是内核和用户空间混合的方式,devtmpfs生成基本的设备节点,udev再根据需要修改权限或名称等。
看了Gooch本人写的devfs介绍,个人认为devfs提出的一些思想实际上和之后的udev,sysfs有一些共通之处。比如层次目录结构就在sysfs中被采用了;而将设备插入拔出事件通知到用户空间则在udev中也体现出来。而且Gooch已经考虑过udev的实现方式来解决问题,也就是内核中产生事件,守护进程处理事件来产生/dev目录。但是他也提到了这种方式相应的缺点。一是需要一个数据库来存储这类事件,而一种自然的实现方式就是文件系统来表现这类事件。二是设备驱动和设备节点之前没有绑定了,需要其它方式来解决。就是sysfs解决了这些问题之后,udev才有可能实现。
参考资料
对于这些概念的其它小结
https://www.binss.me/blog/sysfs-udev-and-Linux-Unified-Device-Model/ 比较全面地提到了devfs,udev和devtmpfs之间的关系,还有一部分代码解释。
http://blog.chinaunix.net/uid-27717694-id-3568856.html 更早的对比devfs和udev的文章。
远古时期的设备创建方法
https://www.linuxjournal.com/article/2597 介绍了/dev目录,以及设备节点的创建方法。
https://www.linuxjournal.com/article/2476 驱动的一般写法。
https://www.linuxjournal.com/article/1219
MAKEDEV相关信息
https://unix.stackexchange.com/questions/396038/why-cant-i-find-makedev-in-the-dev-folder
https://unix.stackexchange.com/questions/356000/why-is-the-name-of-the-makedev-script-spelled-in-all-caps
https://man.openbsd.org/MAKEDEV.8
devfs相关信息
https://www.linuxjournal.com/article/6035 介绍了devfs的一些优点。
https://www.linuxjournal.com/article/4109 关于Linux 2.4的介绍,提及了devfs的优缺点。
https://static.lwn.net/2000/features/OLS/ Ottawa Linux Symposium 2000的总结,提到了没有很多发行版采用devfs。
https://lwn.net/2001/features/OLS/pdf/pdf/devfs.pdf Richard Gooch本人编写的devfs介绍。
https://lwn.net/Articles/15426/ 对于devfs代码质量的简述,大量代码没有被使用。被使用的代码也只局限在小范围。
https://lwn.net/Articles/15425/ 讨论是否要完全移除devfs。
udev
https://www.kernel.org/doc/mirror/ols2003.pdf Ottawa Linux Symposium 2003的总结,其中有Greg Kroah-Hartman写的udev介绍。
https://lwn.net/Articles/28526/ udev 0.1发布,用来代替devfs。
https://lwn.net/Articles/28897/ udev和devfs的初次交锋。
https://lwn.net/Articles/50731/ devfs开始被遗弃。
https://lwn.net/Articles/65197/ Greg Kroah Hartman表示作为udev的开发者,完全不在乎devfs。当初只是为了介绍udev,才选择将它和devfs进行对比,因为很多人熟悉devfs。
https://lwn.net/Articles/139595/ devfs正式被终结。
https://www.linuxjournal.com/article/7496 集合了众多争论udev和devfs的链接。
devtmpfs
https://lwn.net/Articles/330985/ 发布devtmpfs。
https://lwn.net/Articles/331818/ 对于devtmpfs介绍,解答疑虑,说明不是devfs2。