linux 6.5 udev是Linux内核的设备管理器;它主要的功能是管理“/dev”目录底下的设备节点,它同时也是用来接替devfs(设备文件系统)及hotplug(热拔插)的功能;在传统的Linux系统中,“/dev”目录下的设备节点为一系列静态存在的文件,而“udev”则动态提供了在系统中实际存在的设备节点。
linux 6.5 udev是什么?
udev udevadm介绍及linux设备重命名和自动挂载应用实例分析
(一)UDEV介绍
udev在维基百科中的定义是:udev (userspace /dev) is a device manager for the Linux kernel. As the successor of devfsd and hotplug, udev primarily manages device nodes in the /dev directory. At the same time, udev also handles all user space events raised when hardware devices are added into the system or removed from it, including firmware loading as required by certain devices。用中文来说就是:udev是Linux内核的设备管理器。它主要的功能是管理/dev目录底下的设备节点。它同时也是用来接替devfs(设备文件系统)及hotplug(热拔插)的功能,这意味着它要在添加/删除硬件时处理/dev目录以及所有用户空间的行为,包括加载firmware时。
(1)udev概要:
在传统的Linux系统中,/dev目录下的设备节点为一系列静态存在的文件,而udev则动态提供了在系统中实际存在的设备节点。虽然devfs提供了类似功能,但udev有比devfs更加好的地方:
-
udev支持设备的固定命名,而并不依赖于设备插入系统的顺序。默认的udev设置提供了存储设备的固定命名。可以使用其vid(vendor)、pid(device)、设备名称(model)等属性或其父设备的对应属性来确认某一设备。
-
udev完全在用户空间执行,而不是像devfs在内核空间一样执行。结果就是udev将命名策略从内核中移走,并可以在节点创建前用任意程序在设备属性中为设备命名。
(2)udev运行方式:
udev是一个通用的内核设备管理器。它以守护进程的方式运行于Linux系统,并监听在新设备初始化或设备从系统中移除时,内核(通过netlink socket)所发出的uevent。
系统提供了一套规则用于匹配可发现的设备事件和属性的导出值。匹配规则可能命名并创建设备节点,并运行配置程序来对设备进行设置。udev规则可以匹配像内核子系统、内核设备名称、设备的物理等属性,或设备序列号的属性。规则也可以请求外部程序提供信息来命名设备,或指定一个永远一样的自定义名称来命名设备,而不管设备什么时候被系统发现.
(3)udev系统架构:
udev系统可以分为三个部分:
-
libudev函数库,可以用来获取设备的信息。
-
udevd守护进程,处于用户空间,用于管理虚拟/dev
-
管理命令udevadm,用来诊断出错情况。
系统获取内核通过netlink socket发出的信息。早期的版本使用hotplug,并在/etc/hotplug.d/default添加一个链接到自身来达到目的。
(二)udevadm 命令介绍
在Linux man page 中它是这么描述的。udevadm - udev management tool 。也就是说udevadm命令是管理udev的一个工具。实际我们如果要实现设备的重命名或是设备的自动挂载,我们也是使用udevadm来查看和跟踪udev的信息。
udevadm可以用来监视和控制udev运行时的行为,请求内核事件,管理事件队列,以及提供简单的调试机制。
(1)udevadm主命令:
-
info 查询sysfs或者udev的数据库
-
trigger 从内核请求events
-
settle 查看udev事件队列,如果所有的events已处理则退出
-
control 修改udev后台的内部状态信息
-
monitor 监控内核的uevents
-
hwdb 处理硬件数据库索引
-
test 调试
(2)命令应用:
(a)查看设备信息:
udevadm info --query=all --name=sda 查询sda的所有信息
udevadm info --query=path --name=sda 查看sda的path
udevadm info --attribute-walk --name=/dev/nvme0n1 查看nvme0n1的所有父设备一直到sysfs的根节点
--query=type 从数据库中查询指定类型的设备。需要--path和--name来指定设备。合法的查询文件是:设备名,链接,路径,属性
--path=devpath 设备的路径
--name=file 设备节点或者链接
--attribute-walk 打印指定设备的所有sysfs记录的属性,以用来udev规则匹配特殊的设备。该选项打印链上的所有设备信息,最大可能到sys目录。
--device-id-of-file=file 打印主/从设备号
--export-db 输出udev数据库中的内容
(b)监控设备事件:
udevadm monitor [options] 监听内核事件和udev发送的events事件。打印事件发出的设备。可以通过比较内核或者udev事件的时间戳来分析事件时序。
udevadm monitor --property 输出事件的属性
udevadm monitor --kernel --property --subsystem-match=usb 过滤监听符合条件的时间
--kernel 输出内核事件
--udev 输出udev规则执行时的udev事件
--property 输出事件的属性
--subsystem-match=string 通过子系统或者设备类型过滤事件。只有匹配了子系统值的udev设备事件通过。
--tag-match=string 通过属性过滤事件,只有匹配了标签的udev事件通过。
(c)模拟一个udev事件
udevadm test [options] devpath 模拟一个udev事件,打印出debug信息。
(d)接收内核发送来的设备事件
udevadm trigger [options] 接收内核发送来的设备事件。主要用于重放coldplug事件信息
(内核在启动时已经检测到了系统的硬件设备,并把硬件设备信息通过sysfs内核虚拟文件系统导出。udev扫描sysfs文件系统,根据硬件设备信息生成热插拔(hotplug)事件,udev再读取这些事件,生成对应的硬件设备文件。由于没有实际的硬件插拔动作,所以这一过程被称为coldplug。)
--verbose 输出将要被触发的设备列表。
--dry-run 不真的触发事件
--type=type 触发一个特殊的设备。合法的类型:devices,subsystem,failed.默认是devices
--action=action 被触发的事件,默认是change
--subsystem-match=subsystem 触发匹配子系统的设备事件。这个选项可以被多次指定,并且支持shell模式匹配。
--attr-match=attribute=value 触发匹配sysfs属性的设备事件。如果属性值和属性一起指定,属性的值可以使用shell模式匹配。如果没有指定值,会重新确认现有属性。这个选项可以被多次指定。
--attr-nomatch=attribute=value 不要触发匹配属性的设备事件。如果可以使用模式匹配。也可以多次指定
--property-match=property=value 匹配属性吻合的设备。可以多次指定支持模式匹配
--tag-match=property 匹配标签吻合的设备。可以多次指定。
--sysname-match=name 匹配sys设备名相同的设备。可以多次指定支持模式匹配。
(e)查看udev事件队列
udevadm settle [options] 查看udev事件队列,如果所有事件全部处理完就退出。
--timeout=seconds 等待事件队列空的最大时间。默认是180秒。如果是0则立即退出。
--seq-start=seqnum 只等待到给定的顺序号。
--seq-end=seqnum 只等待到给定顺序号之前。
--exit-if-exists=file 如果文件存在就退出
--quiet 不输出任何信息
(二)应用实例:
使用udev实现USB,SD卡设备的重命名、自动挂载、自动卸载。
该功能的简单应用可以参考另外一博客《udev 重命名设备节点名 自动挂载、卸载存储设备分区》
在本文中主要记录在使用时遇到的问题以及注意事项。
(三)注意事项:
(1)设备冲突问题:
在海思平台,对于有些SD卡或是USB自动挂载有些时候会出现冲突的问题,问题现象就是自动挂载的时候有时候有时候U盘和SD卡自动挂载相互干扰。主要原因点是因为海思的HI35XX的很多设备不具备SDIO总线,所以如果要使用SD的设备,一般都是将SD卡通过读卡器转换为USB总线信号。对于对于这类问题,可以通过SD卡的读卡器ID来区分是USB还是SD卡设备。
在海思平台可以使用下面命令查看USB设备信息:
lsusb:
~ # lsusb
Bus 001 Device 002: ID 1c9e:9b3c
Bus 001 Device 003: ID 05e3:0610
Bus 001 Device 001: ID 1d6b:0002
Bus 002 Device 001: ID 1d6b:0001
Bus 001 Device 008: ID 0951:1642
Bus 001 Device 009: ID 05e3:0716
Bus 001 Device 006: ID 0bda:8176
~ #
cat device
~ # cat /sys/kernel/debug/usb/devices
T: Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2
B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 1.10 Cls=09(hub ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=1d6b ProdID=0001 Rev= 3.10
S: Manufacturer=Linux 3.10.0 ohci_hcd
S: Product=HIUSB OHCI
S: SerialNumber=hiusb-ohci
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 2 Ivl=255ms
T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=480 MxCh= 2
B: Alloc= 0/800 us ( 0%), #Int= 5, #Iso= 0
D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=1d6b ProdID=0002 Rev= 3.10
S: Manufacturer=Linux 3.10.0 ehci_hcd
S: Product=HIUSB EHCI
S: SerialNumber=hiusb-ehci
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl=256ms
T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=1c9e ProdID=9b3c Rev= 3.18
S: Manufacturer=LONGSUNG
S: Product=USB Modem
C:* #Ifs= 5 Cfg#= 1 Atr=80 MxPwr=500mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=option
E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E: Ad=83(I) Atr=03(Int.) MxPS= 10 Ivl=32ms
E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms
E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms
E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=GobiNet
E: Ad=89(I) Atr=03(Int.) MxPS= 8 Ivl=32ms
E: Ad=88(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#= 3 Spd=480 MxCh= 4
D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=02 MxPS=64 #Cfgs= 1
P: Vendor=05e3 ProdID=0610 Rev=32.98
S: Product=USB2.0 Hub
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA
I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=01 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 1 Ivl=256ms
I:* If#= 0 Alt= 1 #EPs= 1 Cls=09(hub ) Sub=00 Prot=02 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 1 Ivl=256ms
T: Bus=01 Lev=02 Prnt=03 Port=00 Cnt=01 Dev#= 8 Spd=480 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=0951 ProdID=1642 Rev= 1.00
S: Manufacturer=Kingston
S: Product=DT 101 G2
S: SerialNumber=001CC0EC32F7BB40F71300BF
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=200mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
T: Bus=01 Lev=02 Prnt=03 Port=02 Cnt=02 Dev#= 9 Spd=480 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=05e3 ProdID=0716 Rev=97.27
S: Manufacturer=Genesys
S: Product=USB Reader
S: SerialNumber=000000000013
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
T: Bus=01 Lev=02 Prnt=03 Port=03 Cnt=03 Dev#= 6 Spd=480 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=0bda ProdID=8176 Rev= 2.00
S: Manufacturer=Realtek
S: Product=802.11n WLAN Adapter
S: SerialNumber=00e04c000001
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA
I:* If#= 0 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=ff Prot=ff Driver=rtl8192cu
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=84(I) Atr=03(Int.) MxPS= 64 Ivl=125us
~ #
查看上面信息可以知道USB相关的设备有:
Product=HIUSB EHCI #海思USB总线
Product=USB Modem #上网模块
Product=USB2.0 Hub #USB HUB
Product=DT 101 G2 # U盘
Product=USB Reader # usb 读卡器
Product=802.11n WLAN Adapter #USB网卡
(2)多属性匹配问题
对于一个设备如果要匹配他的多个属性,或者是同一个属性,它在不同的层级中有不同的值,那么这个时候需要使用GOTO功能。比如在一个设备中要重名名一个USB设备,它的命令如下:
KERNEL=="sd*",KERNELS=="*:0:0:1",ATTRS{scsi_level}=="0" ,ATTRS{product}=="USB Reader",ATTRS{idVendor}=="05e3",ATTRS{idProduct}=="0716",SYMLINK+="usbsda%n",OPTIONS="ignore_remove"
这里涉及到多个属性ATTRS 值的匹配,另外该USB因为有些经过了usbHUB,所以他的idVendor 和 idProduct 有多个,就有总线的,也有HUB的,还有USB设备的,这样的情况下是匹配不上设备的。具体是什么原因我没有找到一个合理的解释,但是我又一个可用的解决方案,那就是使用GOTO。将上面命令改成如下就可以了。
KERNEL=="sd*",KERNELS=="*:0:0:1",ATTRS{scsi_level}=="0" GOTO="hisi_end"
ATTRS{product}=="USB Reader",ATTRS{idVendor}=="05e3",ATTRS{idProduct}=="0716",SYMLINK+="usbsda%n",OPTIONS="ignore_remove"
LABEL="hisi_end"
(3)监控设备事件
udev的一些匹配规则有些时候比较的莫名其妙,我也没有找到哪里有比较详细的说明,网上的介绍都它过于简单,实际应用的时候还是很多的问题,比如针对上面介绍的设备,如果要写一条卸载设备的命令,可以使用下面语句:
ACTION=="remove",KERNELS=="*:0:0:1",ATTRS{idVendor}=="05e3",ATTRS{idProduct}=="0716",RUN+="/bin/umount -l /opt/usb_sd1_1"
但是在实际使用的时候,他们匹配不上,ACTION=="remove",KERNELS=="*:0:0:1"与ATTRS{idVendor}=="05e3",ATTRS{idProduct}=="0716" 不能同时使用,如果直接改成:
ACTION=="remove",KERNELS=="*:0:0:1",RUN+="/bin/umount -l /opt/usb_sd1_1"
命令功能可以实现,但是这样会出现于设备冲突的情况,在拔出该设备的时候,会把KERNELS=="*:0:0:1" 的其他设备也卸载掉。
在这个时候,我们可以使用 devadm monitor --property 去监控设备拔出的时候它有哪些事件,有哪些属性可以被捕捉到并且与其他的设备属性不同以便区分不同的设备。下面是截取到的一部分数据:
[10:00:33]KERNEL[1555639338.737818] remove /devices/platform/hiusb-ehci.0/usb1/1-2/1-2.3 (usb)
[10:00:33]UDEV_LOG=3
[10:00:33]ACTION=remove
[10:00:33]DEVPATH=/devices/platform/hiusb-ehci.0/usb1/1-2/1-2.3
[10:00:33]SUBSYSTEM=usb
[10:00:33]DEVNAME=bus/usb/001/007
[10:00:33]DEVTYPE=usb_device
[10:00:33]PRODUCT=5e3/716/9727
[10:00:33]TYPE=0/0/0
[10:00:33]BUSNUM=001
[10:00:33]DEVNUM=007
[10:00:33]SEQNUM=1024
[10:00:33]MAJOR=189
[10:00:33]MINOR=6
从上面可以看出,拔出的时候有个PRODUCT ,它是 idVendor 和 idProduct 值的一个组合,可以使用它来区分不同的设备。最终可以正常卸载设备的命令如下:
ACTION=="remove",SUBSYSTEM=="usb",ENV{PRODUCT}=="5e3/716/9727",RUN+="/bin/umount -l /opt/usb_sd1_1"