Udev
内核几乎可以添加或删除运行系统中的任何设备。设备状态的更改(无论插入还是移除设备)需要传播给用户空间。插入及识别设备后需要对其进行配置。某个设备已识别状态的任何更改都需要通知给此设备的用户。udev
负责在 用户空间,根据用户设置的规则,在监测到设备被插入后,在 /dev/
下自动创建并命名设备文件节点(实际上只能做一个已存在节点的链接文件),也可以自动设置设备属性。
udev
命令
udevadm monitor
可以用于将驱动程序核心事件和 udev 事件处理的计时可视化。
UEVENT[1185238505.276660] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1 (usb)
UDEV [1185238505.279198] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1 (usb)
UEVENT[1185238505.279527] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0 (usb)
UDEV [1185238505.285573] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0 (usb)
UEVENT[1185238505.298878] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10 (input)
UDEV [1185238505.305026] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10 (input)
UEVENT[1185238505.305442] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10/mouse2 (input)
UEVENT[1185238505.306440] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10/event4 (input)
UDEV [1185238505.325384] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10/event4 (input)
UDEV [1185238505.342257] add /devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10/mouse2 (input)
UEVENT
行显示内核已经通过 netlink 发送的事件。UDEV
行显示已经完成的 udev 事件处理程序。计时以微秒为单位显示。UEVENT
和 UDEV
之间的时间是 udev 用于处理此事件或者 udev 守护程序延迟执行从而同步此事件与相关以及已运行的事件的时间。例如,硬盘分区的事件总是等待主磁盘设备事件完成,因为分区事件可能依赖于主磁盘事件从硬件查询的数据。
udevadm monitor --env
显示完整的事件环境。
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/input/input10
SUBSYSTEM=input
SEQNUM=1181
NAME="Logitech USB-PS/2 Optical Mouse"
PHYS="usb-0000:00:1d.2-1/input0"
UNIQ=""
EV=7
KEY=70000 0 0 0 0
REL=103
MODALIAS=input:b0003v046DpC03Ee0110-e0,1,2,k110,111,112,r0,1,8,amlsfw
udevadm info --name=/dev/ttyUSB0
查看指定设备的信息。
udevadm info --name=/dev/ttyUSB0 --attribute-walk
遍历设备信息目录,逐层显示所有设备属性。
udev
规则
udev
规则可以与内核添加到事件本身的属性或者内核导出到 sysfs
的任何信息匹配。规则还可以从外部程序请求其他信息。根据提供的规则匹配每个事件。所有规则都位于 /etc/udev/rules.d
目录下。
规则文件中的每一行至少包含一个键值对。有两种类型的键,匹配键和指派键。如果所有匹配键与它们的值匹配,则应用此规则并将指派键指派给特定的值。匹配规则可以指定设备节点的名称、添加指向该节点的符号链接或者运行作为事件处理一部分的特定程序。如果找不到匹配的规则,则使用默认设备节点名来创建设备节点。udev
手册页中描述了有关规则语法和提供用来与数据匹配或导入数据的键的详细信息。以下示例规则提供了 udev
规则语法的基本介绍。这些示例规则全部取自 /etc/udev/rules.d/50-udev-default.rules
下的 udev
默认规则集。
# console
KERNEL=="console", MODE="0600", OPTIONS="last_rule"
# serial devices
KERNEL<mark>"ttyUSB*", ATTRS{product}</mark>"[Pp]alm*Handheld*", SYMLINK+="pilot"
# printer
SUBSYSTEM<mark>"usb", KERNEL</mark>"lp*", NAME="usb/%k", SYMLINK+="usb%k", GROUP="lp"
# kernel firmware loader
SUBSYSTEM<mark>"firmware", ACTION</mark>"add", RUN+="firmware.sh"
console
规则由三个键构成:一个匹配键 (KERNEL
) 和两个赋值键(MODE
、OPTIONS
)。KERNEL
匹配规则搜索设备列表以查找类型为 console
的所有项。只有完全匹配才有效,才能触发执行此规则。在这种情况下,MODE
键为设备节点指派特殊权限,仅为此设备的拥有者指派读写权限。OPTIONS
键将该规则标记为此类型的所有设备最后采用的规则。匹配此特殊设备类型的任何后续规则都不产生任何影响。
50-udev-default.rules
中不再提供 serial devices
规则,但该规则仍然值得考虑。该规则由两个匹配键(KERNEL
和 ATTRS
)和一个赋值键 (SYMLINK
) 构成。KERNEL
键搜索类型为 ttyUSB
的所有设备。该键使用 *
通配符匹配这些设备中的几个。第二个匹配键 ATTRS
检查任何 ttyUSB
设备的 sysfs
中的 product
属性文件是否包含特定字符串。赋值键 (SYMLINK
) 将符号链接添加至该设备的 /dev/pilot
下。此键中使用的运算符 (+=
) 告知 udev 进一步执行此操作,即使前面或后面的规则添加其他符号链接。由于此规则包含两个匹配键,因此仅当两个条件都满足时,才应用。
printer
规则处理 USB 打印机,其中包含两个匹配键(SUBSYSTEM
和 KERNEL
),并且必须同时应用这两个键,才能应用整个规则。三个赋值键处理该设备类型的命名 (NAME
)、符号设备链接 (SYMLINK
) 的创建,以及此设备类型的组成员资格 (GROUP
)。在 KERNEL
键中使用通配符 *
将使其匹配若干 lp
打印机设备。NAME
和 SYMLINK
键中都使用了替换项,以便按内部设备名称扩展这些字符串。例如,指向第一个 lp
USB 打印机的符号链接为 /dev/usblp0
。
kernel firmware loader
规则用于使 udev 在运行时期间通过外部助手脚本装载其他固件。SUBSYSTEM
匹配键搜索 firmware
子系统。ACTION
键检查是否添加了属于 firmware
子系统的任何设备。RUN+=
键触发执行 firmware.sh
脚本,以便找到应装载的固件。
所有规则具有一些共同的特征:
- 每个规则由一个或多个以逗号分隔的键值对构成。
- 键的运算由运算符确定。
udev
规则支持多个运算符。 - 每个给定值必须用引号引起来。
- 规则文件的每一行代表一个规则。如果某个规则超过一行,请使用
\
合并不同行,就像在外壳语法中一样。 -
udev
规则支持与*
、?
和[]
模式匹配的外壳式模式。 -
udev
规则支持替换。
/dev
目录
/dev
目录中的设备节点提供对相应的内核设备的访问。使用 udev
时,/dev
目录反映内核的当前状态。每个内核设备都有相应的设备文件。如果设备从系统断开,则删除此设备节点。
/dev
目录的内容保存在临时文件系统中,所有文件都是在每个系统启动时提供的。手动创建或修改的文件在重引导时是有意不保存的。无论可使用 systemd-tmpfiles 创建的相应内核设备状态如何,静态文件和目录都始终应位于 /dev
目录中。配置文件位于 /usr/lib/tmpfiles.d/
和 /etc/tmpfiles.d/
中。
内核 uevents
和 udev
必需的设备信息由 sysfs
文件系统导出。对于内核检测到并已初始化的设备,将创建一个带有该设备名称的目录。它包含带有特定于设备属性的属性文件。
每次添加或删除设备时,内核都会发送 uevent 来向 udev
通知更改。一旦启动,udev
守护程序便会读取和分析 /etc/udev/rules.d/*.rules
文件中提供的所有规则,并将它们保留在内存中。如果更改、添加或去除了规则文件,则守护程序可以使用命令 udevadm control --reload
重新装载所有规则在内存中的表示形式。
每个接收到的事件都根据所提供的规则集进行匹配。这些规则可以增加或更改事件环境键、为要创建的设备节点请求特定名称、添加指向该节点的符号链接或者添加设备节点创建后运行的程序。从内核 netlink 套接字接收驱动程序核心 uevent
。
驱动程序、内核模块和设备
设备的内核总线驱动程序探测。对于每个检测到的设备,内核都会在驱动程序核心将 uevent 发送到 udev
守护程序时创建内部设备结构。总线设备通过特殊格式的 ID 来标识自己,这可以识别设备的类型。通常,这些 ID 由供应商和产品 ID 以及其他特定于子系统的值组成。每个总线都有自己对于这些 ID 的方案,称为 MODALIAS
。内核获取设备信息,由此组成一个 MODALIAS
ID 字符串,并将该字符串与事件一起发送。对于 USB 鼠标,如下所示:
MODALIAS=usb:v046DpC03Ed2000dc00dsc00dp00ic03isc01ip02
每个设备驱动程序都带有它可以处理的设备的已知别名列表。这个列表包含在内核模块文件中。程序 depmod 读取 ID 列表并在内核的 /lib/modules
目录中为所有当前可用的模块创建文件 modules.alias
。使用这种基础结构,模块的装载就如为每个带有 MODALIAS
键的事件调用 modprobe
一样简单。如果调用 modprobe $MODALIAS
,它将组成该设备的设备别名与模块提供的别名相匹配。如果找到匹配的项,则装载该模块。所有这些操作均由 udev
自动触发。
引导和启动设备设置
在 udev
守护程序运行之前的引导进程中发生的所有设备事件都会丢失,因为处理这些事件的基础结构保存在 root 文件系统中,并且此时不可用。为了弥补此损失,内核提供了一个 uevent
文件,该文件位于 sysfs 文件系统每个设备的设备目录中。通过将 add
写入到该文件,内核将再次发送引导时丢失的相同事件。/sys
触发器中所有 uevent
文件的简单循环将再次触发所有事件来创建设备节点并执行设备设置。
例如,在引导期间出现的 USB 鼠标可能不会由早期引导逻辑初始化,因为驱动程序在那时不可用。此设备发现的事件丢失并且不能为该设备查找内核模块。您无需手动搜索连接的设备,udev
会在根文件系统可用后向内核请求所有设备事件,这样 USB 鼠标设备的事件就会再次运行。现在它在装入的 root 文件系统上找到内核模块,因此可以初始化 USB 鼠标。
在用户空间中,设备冷插入序列和运行时期间发现的设备之间没有明显的区别。在这两种情况下,使用相同的规则来匹配并且运行相同的配置程序。
参考链接
- https://documentation.suse.com/zh-cn/sles/15-GA/html/SLES-all/cha-udev.html#sec-udev-persdev
- https://blog.csdn.net/woyimibayi/article/details/78320915 #待整理笔记