4. devtmpfs与设备节点自动创建
在早期的 Linux 系统中,/dev 目录下的设备节点需要由发行版维护者预先创建,或者由用户手动执行 mknod 命令生成。这种方式存在两个明显问题:一是内核新增设备后,用户空间无法立即感知;二是静态创建的节点数量庞大、主次设备号容易冲突。为了解决这些问题,Linux 内核引入了 devtmpfs,并与用户空间的 udev 配合,形成了今天主流的设备节点自动创建机制。
一、devtmpfs 的核心原理
devtmpfs 是一种基于内存的虚拟文件系统,通常挂载在 /dev 目录。它由内核直接维护,而不是由用户空间程序维护。当内核检测到新设备注册时,会在 devtmpfs 中自动创建对应的设备节点,包括字符设备节点和块设备节点,并赋予正确的设备号。
与传统的 tmpfs 或磁盘文件系统不同,devtmpfs 的节点创建动作发生在内核态。设备驱动调用 device_register() 或 cdev_add() 等接口后,内核的设备模型核心会触发 devtmpfs 的创建逻辑,最终在 /dev 下生成节点。这意味着即使 udev 尚未启动,系统也已经拥有基本的设备节点,从而保证根文件系统挂载、控制台访问等早期启动流程能够正常工作。
二、内核自动创建节点的过程
内核自动创建节点的过程可以概括为“设备注册 → 设备模型通知 → devtmpfs 创建节点”。当驱动注册一个设备时,内核会为该设备分配 struct device,并把它加入设备层次结构。随后,内核通过 kobject_uevent() 向用户空间发送 uevent 事件,同时 devtmpfs 根据设备的主次设备号创建设备文件。
下面是一个简化的观察过程,展示如何确认 devtmpfs 是否挂载,以及如何查看内核创建的节点:
# 查看 /dev 的挂载类型
mount | grep ' /dev '
# 典型输出:
# devtmpfs on /dev type devtmpfs (rw,nosuid,relatime,size=...)
# 查看内核创建的设备节点
ls -l /dev/null /dev/tty /dev/sda
# crw-rw-rw- 1 root root 1, 3 ... /dev/null
# crw-rw-rw- 1 root root 5, 0 ... /dev/tty
# brw-rw---- 1 root disk 8, 0 ... /dev/sda
# 查看内核 uevent 事件(需要 root 权限)
udevadm monitor --kernel --property
从输出可以看到,/dev/null 的主次设备号为 1,3,/dev/tty 为 5,0,/dev/sda 为 8,0。这些节点由内核在设备注册时自动创建,不需要用户手动执行 mknod。
三、devtmpfs 与 udev 的协作流程
虽然 devtmpfs 能够自动创建节点,但它只负责“创建基础节点”,并不负责复杂的命名、权限、属主和符号链接管理。这些工作由用户空间的 udev 完成。两者的协作流程如下:
- 内核检测到设备,注册
struct device,并在devtmpfs中创建初始设备节点。 - 内核通过 netlink 套接字发送 uevent 事件,事件中包含设备路径、主次设备号、子系统等信息。
udevd守护进程接收 uevent,读取/sys下对应的设备属性。udevd根据/etc/udev/rules.d/和/lib/udev/rules.d/中的规则,决定是否重命名节点、修改权限、创建符号链接或执行额外程序。- 最终在
/dev下形成符合系统策略的设备节点和链接。
下面的 SVG 展示了从内核设备注册到 udev 完成节点管理的完整信号与数据流关系:
四、典型场景与配置示例
在实际系统中,devtmpfs 与 udev 的协作非常常见。例如插入一个 USB 存储设备时,内核首先识别设备并创建 /dev/sdb 节点,随后 udev 根据规则创建 /dev/disk/by-uuid/ 下的符号链接,并设置正确的权限。这样应用程序既可以使用稳定的 UUID 路径,也可以使用传统的 /dev/sdb 路径。
下面是一个 udev 规则示例,用于为特定 USB 设备创建固定名称的符号链接:
# /etc/udev/rules.d/99-usb-disk.rules
SUBSYSTEM=="block", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678", \
SYMLINK+="myusbdisk", MODE="0660", GROUP="disk"
加载规则后,重新插拔设备,即可看到 /dev/myusbdisk 符号链接指向实际设备节点。这个过程中,devtmpfs 已经创建了 /dev/sdb,而 udev 在此基础上增加了符号链接和权限控制。
五、常见误区与复盘
| 误区 | 正确理解 |
|---|---|
| 认为 udev 负责创建所有设备节点 | 基础节点由内核 devtmpfs 创建,udev 负责后续管理 |
| 认为 devtmpfs 可以替代 udev | devtmpfs 不处理命名策略、权限和符号链接,无法替代 udev |
| 手动 mknod 创建节点后,udev 会覆盖 | udev 可能根据规则重新调整,手动节点应避免与规则冲突 |
| 禁用 devtmpfs 后系统仍能正常启动 | 禁用后需要依赖静态节点或 initramfs 中的其他机制,风险较高 |
CONFIG_DEVTMPFS,系统可能无法自动创建 /dev 下的基础节点,导致启动过程中找不到控制台或根设备。现代发行版通常默认启用该选项。
总结来说,devtmpfs 解决了“设备节点何时创建”的问题,udev 解决了“设备节点如何管理”的问题。两者分工明确:内核负责基础可用性,用户空间负责策略与灵活性。理解这一协作流程,是掌握 Linux 用户空间设备管理的关键一步。