【udev】关于/dev 设备节点的生成 &udev

1. 设备节点的生成机制

Linux 中的设备节点(例如 /dev/ttyS0、/dev/sda 等)本质上是特殊的文件,它们代表内核中的设备,用户态程序通过这些节点与设备通信。

1.1 内核初始化:使用 devtmpfs 创建节点

  • 内核加载某个驱动后,调用 device_register() 等函数注册设备。
  • 同时通过 devtmpfs 文件系统自动创建 /dev/ 下的设备节点
  • devtmpfs 是内核在启动 early stage 时就挂载的临时内存文件系统。

关于devtmpfs
现代主流 Linux 操作系统中 ,/dev 下的设备节点大都是kernel基于 devtmpfs 创建的。devtmpfs 是在内核启动的中早期阶段初始化的,通常在多数驱动加载前完成挂载 ,并在驱动加载过程中动态创建设备节点。在内核早期初始化阶段, 调用 devtmpfs_init()创建一个内存中的虚拟文件系统(tmpfs 类型)。

可以通过以下命令查看操作系统是否支持devtmpfs:

bash 复制代码
grep DEVTMPFS /boot/config-$(uname -r)
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y # 如果 CONFIG_DEVTMPFS_MOUNT=y,内核会自动挂载 /dev

我们以串口举例,/dev/ttyS0 事实上是由linux 内核在调用device_register() 时 生成的, 和udev 是没有关系的。 通常, 此时udev这种用户级进程还没有启动起来。

使用mount 命令查看

bash 复制代码
mount | grep devtmpfs
udev on /dev type devtmpfs (rw,nosuid,relatime,size=7916552k,nr_inodes=1979138,mode=755)

这说明 当前系统的 /dev 是挂载在 devtmpfs 上的。systemd-udevd 用户空间服务"管理 "这个挂载点,但挂载本身由内核完成

2. 关于udev

既然devtmpfs 已经创建 了设备节点,为什么还需要udev 呢? 我们给出以张表

功能 是否由 devtmpfs 实现 是否由 udev 实现
创建基础设备文件(如 /dev/ttyS0) 有时也创建(如特殊权限/别名)
设置权限、属主、属组(如 dialout) 不行 可以
创建符号链接(如 /dev/serial/by-id/)) 不行 负责
重命名设备或动态处理热插拔 不行 负责

由此可见,udev 必不可少, 虽然devtmpfs 已经创建 了设备节点,但udev 负责管理这些设备节点。

udev 通过监听 内核通知用户空间"设备事件"uevent(基于netlink) 来实现对设备的节点的管理。

当设备**注册(register)或注销(unregister)**时,内核通过调用:

c 复制代码
kobject_uevent(&kobj, KOBJ_ADD);
kobject_uevent(&kobj, KOBJ_REMOVE);

来产生用户空间可见的事件。

2.1 uevent 的事件类型(常见)

内核宏 udev 中看到的 ACTION 说明
KOBJ_ADD add 设备添加(如插入 USB)
KOBJ_REMOVE remove 设备移除(如拔掉 USB)
KOBJ_CHANGE change 设备属性变更(如状态/权限变化)
KOBJ_MOVE move 更换路径(罕见)
KOBJ_ONLINE online 设备上线(用于网络等)
KOBJ_OFFLINE offline 设备离线(同上)

举个例子,插入 /dev/ttyUSB0

  • 设备驱动调用 device_register() 或 register_chrdev_region()
  • 内核通过 kobject_uevent(KOBJ_ADD) 生成事件
  • 内核借助 netlink 的 NETLINK_KOBJECT_UEVENT 协议将事件广播到用户空间
  • udevd(systemd-udevd)通过 netlink socket 监听事件
  • 收到后,udevd 读取环境变量并根据 /etc/udev/rules.d/ 匹配规则
  • 如果有匹配项:
    • 执行 RUN+="脚本",或者
    • 设置权限、改名、建立软链等

2.2 udev 工具使用

2.2.1 查看设备信息

bash 复制代码
# 显示与设备 /dev/ttyUSB0 直接对应的 udev 属性信息,只看"当前设备"这一层。
udevadm info /dev/ttyUSB0  
# udevadm info -a -n /dev/ttyUSB0  会 递归显示设备及其所有父设备的属性
#(udev 规则可以使用父设备的属性!) 它列出了所有可能用于写 udev 规则的字段,
# 包括父设备的 ATTRS{}、KERNELS、SUBSYSTEMS 等
udevadm info -a -n /dev/ttyUSB0

编写复杂 udev 规则:你可以通过 ATTRS{serial}、SUBSYSTEMS 等对父设备做更细粒度的过滤。

2.2.2 实时监控内核与 udev 事件

只监控内核事件

bash 复制代码
udevadm monitor --kernel

只监控 udev 事件

bash 复制代码
udevadm monitor --udev

显示环境变量(设备属性)

bash 复制代码
# 显示设备的所有 udev 属性(property),是 udev 设备节点关联的完整属性集,
# 等同于运行 udevadm info --query=property 得到的内容
udevadm monitor --property 
udevadm monitor --environment # 内核(kernel)事件和 udev 事件的环境变量。

其它

你可以通过 udevadm monitor --environment --udev 看到这些变量:

bash 复制代码
UDEV  [552.115431] add      /devices/pci0000:00/0000:00:14.0/usb1/1-1 (usb)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-1
SUBSYSTEM=usb
DEVNAME=/dev/bus/usb/001/002
DEVTYPE=usb_device
PRODUCT=1d6b/2/300
TYPE=9/0/1
...

内核在发送 uevent 时,会构造这些环境变量,并通过 netlink 发送出去

2.2.3 udevadm trigger

udevadm trigger 是 Linux 系统中用于触发内核设备事件(uevent)重新发送的命令,常用于手动触发设备的 add、change、remove 等事件,进而让 udev 重新处理设备节点,执行相关规则和动作。

  • 重新触发内核发送设备事件(uevent),比如重新"添加"或"修改"设备。
  • 让 udev 重新扫描和处理指定设备。
  • 常用于调试、重载设备驱动,或修复设备节点状态异常
bash 复制代码
udevadm trigger --action=add /sys/class/tty/ttyS0
# 让内核重新"触发" /sys/class/tty/ttyS0 设备的 add 事件
# 使 udev 重新处理这个设备(比如重新创建设备节点,运行对应规则和脚本)

2.2.4 模拟设备添加,测试规则逻辑

udevadm test 是一个用于测试和调试 udev 规则的强大工具。它可以模拟 udev 处理设备事件的过程,显示规则匹配和执行的详细信息,但不会实际更改系统状态(不会创建设备节点或运行脚本)。

工作流程

  • udevadm test 读取指定设备的 sysfs 信息
  • 模拟 udev 收到的设备事件(一般是 add)
  • 按照规则文件逐条匹配并打印匹配结果
  • 显示将执行的动作(创建设备节点、运行程序等)
  • 不实际执行这些动作,只打印模拟信息
bash 复制代码
udevadm test /sys/class/tty/ttyS0

2.3 udev 规则

udev 规则文件用于定义如何响应内核设备事件(如添加、移除等),以便创建设备节点、设置权限、运行脚本等。其语法基于匹配键(匹配条件)与赋值键(行为指令)的组合。

基本语法格式:

bash 复制代码
MATCH_KEY=="value", ASSIGN_KEY="value"

MATCH_KEY :匹配条件,只有满足时该规则才会生效
ASSIGN_KEY:指定当规则生效时应执行的操作(如设备权限、运行程序等)

2.3.1 常用匹配键(左侧)

关键字 说明
KERNEL 匹配设备名,如 sda, ttyS0
SUBSYSTEM 匹配子系统,如 block, tty, net
ACTION 匹配事件类型,如 add, remove, change
DEVPATH 匹配 /sys 中设备路径
ATTR{key} 匹配设备属性(来自 sysfs)
ATTRS{key} 匹配父设备属性(用于多层设备匹配)
DRIVER 匹配驱动名
ENV{key} 匹配环境变量(如 ENV{ID_SERIAL}
PROGRAM 执行命令,匹配其退出值
TEST 测试文件是否存在
TAG 匹配指定标签

2.3.2 常用赋值键(右侧)

关键字 说明
NAME 设定设备节点名称
SYMLINK 创建符号链接,如 /dev/serial/by-id/...
OWNER 设定所有者
GROUP 设定所属组
MODE 设定权限(如 0660
RUN+="" 添加要执行的命令或脚本(+ 允许多个)
TAG+="value" 添加标签
ENV{key}="val" 设置环境变量

2.3.3 示例规则解析

bash 复制代码
SUBSYSTEM=="tty", KERNEL=="ttyS0", ACTION=="add", MODE="0660", GROUP="dialout", SYMLINK+="serial0", RUN+="/usr/local/bin/setup_serial.sh"

含义:

  • 当子系统为 tty 且设备名是 ttyS0 且事件是 add 时:
  • 设置权限为 0660
  • 设置所属组为 dialout
  • 创建 /dev/serial0 符号链接指向该设备
  • 执行 /usr/local/bin/setup_serial.sh
bash 复制代码
SUBSYSTEM=="tty", ATTRS{id}=="PNP0501", ACTION=="add", RUN+="/usr/local/bin/fixup_serial.sh"

含义:

  • 匹配的是 tty 子系统,也就是串口设备,如 /dev/ttyS0, /dev/ttyUSB0
  • 匹配该设备的父设备属性 id 为 PNP0501,这是常见的 PC 标准串口标识符
  • 仅当设备 被添加 时触发(比如开机时创建)
  • 触发时执行 /usr/local/bin/fixup_serial.sh 脚本

2.3.4 执行脚本

在 udev 的 RUN+= 脚本(如 /usr/local/bin/fixup_serial.sh)中,可以使用 udev 在事件中提供的一系列 环境变量,这些变量由内核 uevent 和 udev 合并生成,反映了设备的状态和属性。
不是所有在 udev 规则文件中出现的变量,在 RUN+="..." 调用的脚本中都可以直接使用。
常见可用变量列表

变量名 说明
ACTION 事件类型:addremovechange
DEVPATH sysfs 中的设备路径,如 /devices/pnp0/00:02/tty/ttyS0
SUBSYSTEM 子系统名,如 ttyblocknet
DEVNAME 设备节点名称,如 /dev/ttyS0
DEVTYPE 设备类型(如 partition, disk, usb_interface
MAJOR/MINOR 主设备号、次设备号
SEQNUM 事件序号(udev 使用)
ID_SERIAL 串口设备的序列号(某些设备才有)
ID_PATH 唯一物理路径标识(如 pci-0000:00...
ID_VENDOR, ID_MODEL USB 等设备的信息
DRIVER 驱动名称(如果有)

如何查看实际的环境变量?

bash 复制代码
udevadm info -a -n /dev/ttyUSB0
# 或者
udevadm monitor --environment --udev

不能在脚本中直接使用的变量

变量 原因说明
KERNEL 匹配规则时使用,但不是标准环境变量,默认不传入脚本
ATTR{}ATTRS{} 用于匹配设备属性,不自动传入脚本中
ENV{custom} 仅在规则内部设置的环境变量,需显式传给脚本
PROGRAM="..." 的返回值 在规则中可用,脚本中不可见,需手动传递

想在脚本中使用规则中的值怎么办?

bash 复制代码
SUBSYSTEM=="tty", KERNEL=="ttyS0", ENV{MY_PORT}="S0", RUN+="/usr/local/bin/do_something.sh"

然后在脚本中使用:

bash 复制代码
echo "My port is: $MY_PORT"

建议调试方法

你可以在脚本最前面加入:

bash 复制代码
env > /tmp/udev_env.txt
相关推荐
Absinthe_苦艾酒7 分钟前
计算机网络(三)传输层TCP
网络·tcp/ip·计算机网络
望获linux17 分钟前
【实时Linux实战系列】CPU 隔离与屏蔽技术
java·linux·运维·服务器·操作系统·开源软件·嵌入式软件
0wioiw032 分钟前
C#基础(项目结构和编译运行)
linux·运维·服务器
foxhuli22934 分钟前
禁止ifrmare标签上的文件,实现自动下载功能,并且隐藏工具栏
前端
GLAB-Mary1 小时前
AI会取代网络工程师吗?理解AI在网络安全中的角色
网络·人工智能·web安全
青皮桔1 小时前
CSS实现百分比水柱图
前端·css
影子信息1 小时前
vue 前端动态导入文件 import.meta.glob
前端·javascript·vue.js
青阳流月1 小时前
1.vue权衡的艺术
前端·vue.js·开源
样子20181 小时前
Vue3 之dialog弹框简单制作
前端·javascript·vue.js·前端框架·ecmascript
kevin_水滴石穿1 小时前
Vue 中报错 TypeError: crypto$2.getRandomValues is not a function
前端·javascript·vue.js