这里需要理清一个点,就是/dev/下的inode节点,在创建的时候并不需要提供具体的file_operations函数即可创建
只有当open或者操作该节点的时候,虚拟文件系统(VFS)才会根据主次设备号去内核中找到对应的file_operations函数
register_chrdev()则会给一个主设备号下所有的0-255的次设备号都映射同一个file_operations
1. 用户态创建(老的方式)
在相当长的一段时间里,Linux 推崇"策略与机制分离",即内核只负责导出设备信息,由用户态程序决定如何在 /dev 下创建节点、设置什么权限或起什么别名:
-
自动动态创建(
udev/mdev) :当驱动调用device_create()时,内核会在/sys/class/...下生成dev属性文件(记录主次设备号),并通过 Netlink 广播KOBJ_ADD的uevent事件。用户态守护进程(如桌面/服务器的systemd-udevd或嵌入式 BusyBox 的mdev)监听到事件后,在用户态调用mknod()系统调用创建/dev/fb0。 -
手动静态创建(
mknod命令) :即使驱动没有调用device_create(),只要驱动在内核通过register_chrdev()注册了主设备号,管理员也可以直接在终端执行mknod /dev/fb3 c 29 0手动在用户态把节点创建出来。
2. 内核态创建 (现代默认)
从 Linux 2.6.32 开始,内核引入了 devtmpfs 文件系统(对应内核配置项 CONFIG_DEVTMPFS),这让内核态具备了直接创建 /dev 设备节点的能力:
工作机制 :devtmpfs 是一个驻留在内存中的虚拟文件系统,通常在系统启动极早期被挂载到 /dev。当内核中的驱动调用 device_create()(内部调用 device_add())时,如果传入的 dev->devt(主设备号)不为 0,内核中的 devtmpfs 核心线程会直接在 /dev 下创建对应的设备节点文件 (例如 /dev/fb0),完全不需要等待用户态的 udev 启动。
这里只判断主设备号不为 0,是因为只有主设备号不为 0 的设备才需要在 /dev 下暴露给用户态;而次设备号则用于在打开设备时,配合主设备号索引 file_operations 以及区分同一驱动下的不同硬件实例(如 fb0 和 fb1)
3. 现代 Linux 系统中两者的分工协作
现在的linux二者都使用了,但是分工不同
1.devtmpfs负责给注册进系统的拥有设备号的设备在/dev/下创建对应的设备节点
2.用户态的udevd收到内核的uevent后,负责按照如/lib/udev/rules.d下的规则,把对应/dev下的节点的权限进行修改或者创建软链接
关于/dev下的目录
比如注册进input子系统的设备,其暴露在/dev下的设备inode节点则是/dev/input/路径下的
这是因为其class实现了一个给devtmpfs用的回调接口,这个在struct class的源码中就有
c
struct class {
const char *name;
// ...
char *(*devnode)(const struct device *dev, umode_t *mode);
// ...
};
上面的devnode函数的返回值,就是要在/dev下创建的具体inode完整目录