什么是udev?
udev(userspace /dev)是Linux内核的设备管理器,是devfs和hotplug的继承者,主要管理 /dev目录中的设备节点。同时,udev 还处理在将硬件设备添加到系统中或从系统中删除时引发的所有用户空间事件,包括加载某些设备所需的固件等等。
为什么使用udev?
/dev目录是记录系统加载的所有设备文件的地方(Linux 中的所有内容都是文件,设备也是)。 在 Linux 内核版本 2.5 之前,该目录一直由 devfs 文件系统管理。 devfs 的引入解决了一些问题,但是仍然存在不确定的设备映射、/dev目录下文件太多且不能表示当前系统上的实际设备等等问题。
因此udev开始解决以上问题,其具体的目标是:
(1) 在用户空间中运行(节省了因保存设备命名规则而浪费的内核内存空间);
(2) 创建动态 /dev(插入或删除设备时自动创建或删除 /dev 中的设备条目);
(3) 提供一致的设备命名(例如从eth0更改为enp2s0),以及提供用户空间 API 以访问有关当前系统设备的信息。
udev内部结构
虽然 udev 在用户空间中运行,但它与 Linux 内核紧密联系在一起。内核识别到设备插入/删除/更改事件后,通过netlink IPC 机制给用户空间发送通知。
udev 的协议族是kobject_uevent。udev是systemd的一部分,因此它使用 systemd 的套接字协议,如图一所示。udev运行过程中,通过该协议获得socket,然后接收内核发送过来的消息,如图二所示。
[root@localhost secure]# cat /lib/systemd/system/systemd-udevd-kernel.socket
[Unit]
Description=udev Kernel Socket
Documentation=man:systemd-udevd.service(8) man:udev(7)
DefaultDependencies=no
Before=sockets.target
ConditionPathIsReadWrite=/sys
[Socket]
Service=systemd-udevd.service
ReceiveBuffer=128M
ListenNetlink=kobject-uevent 1
PassCredentials=yes
图一 udev的套接字协议
systemd/src/udev/udevd.c:
if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) {
if (netlink >= 0)
return -1;
netlink = fd;
continue;
}
图二 udev中创建netlink套接字
从内核发送的消息如图三所示。除了这里解释的netlink头部外,netlink 数据包的主体只是一个纯字符串。
"add@/class/input/input9/mouse2\0 // message
ACTION=add\0 // action type
DEVPATH=/class/input/input9/mouse2\0 // path in /sys
SUBSYSTEM=input\0 // subsystem (class)
SEQNUM=1064\0 // sequence number
PHYSDEVPATH=/devices/pci0000:00/0000:00:1d.1/usb2/22/22:1.0\0 // device path in /sys
PHYSDEVBUS=usb\0 // bus
PHYSDEVDRIVER=usbhid\0 // driver
MAJOR=13\0 // major number
MINOR=34\0", // minor number
图三 内核发送给udev的消息
udev通过netlink接收到内核发送来的设备相关命令后,开始对设备进行初始化,当初始化完成后,其通过libudev向所有其它用户空间进程广播设备就绪通知。一个简单的udev设备通知操作流程如图四所示。
图四 udev设备通知操作流程图
udev接收到内核驱动发送过来的设备命令后,首先将收到的消息和规则库中的规则匹配,若匹配合适,则根据规则中指定的操作对设备进行初始化,并添加或移除对应的设备文件,初始化完成后,其通知libudev或其他进程设备就绪,可以继续接下来的如加载模块、加载固件等操作。