驱动篇:udev 与 devfs(二)

驱动篇:udev 与 devfs(二)

udev 的组成
udev 目前和 systemd 项目合并在一起了,udev 在用户空间中执行,动态建立 / 删除设备文件,允许每个人都不用关心主 / 次设备号而提供 LSB ( Linux 标准规范, Linux Standard Base )名称,并且可以根据需要固定名称。 udev 的工作过程如下:

1 )当内核检测到系统中出现了新设备后,内核会通过 netlink 套接字发送 uevent 。
2 ) udev 获取内核发送的信息,进行规则的匹配。匹配的事物包括 SUBSYSTEM 、 ACTION 、atttribute 、内核提供的名称(通过 KERNEL= )以及其他的环境变量。

假设在 Linux 系统上插入一个 Kingston 的 U 盘,我们可以通过 udev 的工具 “udevadm monitor–kernel–property–udev” 捕获到的 uevent 包含的信息:

UDEV [6328.797974] add
/devices/pci0000:00/0000:00:0b.0/usb1/1-1/1-
1:1.0/host7/target7:0:0/7:0:0:0/block/sdc (block)
ACTION=add
DEVLINKS=/dev/disk/by-id/usb-Kingston_DataTraveler_2.0_5B8212000047-0:0 /dev/
disk/by-path/pci-0000:00:0b.0-usb-0:1:1.0-scsi-0:0:0:0
DEVNAME=/dev/sdc
DEVPATH=/devices/pci0000:00/0000:00:0b.0/usb1/1-1/1-1:1.0/host7/target7:0:0/7:0:0:0/
block/sdc
DEVTYPE=disk
ID_BUS=usb
ID_INSTANCE=0:0
ID_MODEL=DataTraveler_2.0
ID_MODEL_ENC=DataTraveler\x202.0
ID_MODEL_ID=6545
ID_PART_TABLE_TYPE=dos
ID_PATH=pci-0000:00:0b.0-usb-0:1:1.0-scsi-0:0:0:0
ID_PATH_TAG=pci-0000_00_0b_0-usb-0_1_1_0-scsi-0_0_0_0
ID_REVISION=PMAP
ID_SERIAL=Kingston_DataTraveler_2.0_5B8212000047-0:0
ID_SERIAL_SHORT=5B8212000047
ID_TYPE=disk
ID_USB_DRIVER=usb-storage
ID_USB_INTERFACES=:080650:
ID_USB_INTERFACE_NUM=00ID_VENDOR=Kingston
ID_VENDOR_ENC=Kingston
ID_VENDOR_ID=0930
MAJOR=8
MINOR=32
SEQNUM=2335
SUBSYSTEM=block

我们可以根据这些信息,创建一个规则,以便每次插入的时候,为该盘创建一个 /dev/kingstonUD 的符号链接
设备命名规则范例

# Kingston USB mass storage
SUBSYSTEM=="block", ACTION=="add", KERNEL=="*sd ", ENV{ID_TYPE}=="disk",
ENV{ID_VENDOR}=="Kingston", ENV{ID_USB_DRIVER}=="usb-storage", SYMLINK+="kingstonUD"

插入 kingston U 盘后, /dev/ 会自动创建一个符号链接:

root@barry-VirtualBox:/dev# ls -l kingstonUD
lrwxrwxrwx 1 root root 3 Jun 30 19:31 kingstonUD -> sdc

udev 规则文件
udev 的规则文件以行为单位,以 “#” 开头的行代表注释行。其余的每一行代表一个规则。每个规则分成一个或多个匹配部分和赋值部分。匹配部分用匹配专用的关键字来表示,相应的赋值部分用赋值专用的关键字来表示。匹配关键字包括: ACTION (行为)、 KERNEL (匹配内核设备名)、 BUS (匹配总线类型)、SUBSYSTEM (匹配子系统名)、 ATTR (属性)等,赋值关键字包括: NAME (创建的设备文件名)、SYMLINK (符号创建链接名)、 OWNER (设置设备的所有者)、 GROUP (设置设备的组)、 IMPORT (调用外部程序)、 MODE (节点访问权限)等。

例如,如下规则:

SUBSYSTEM=="net", ACTION=="add", DRIVERS==" *", ATTR{address}=="08:00:27:35:be:ff",
ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"

其中的 “ 匹配 ” 部分包括 SUBSYSTEM 、 ACTION 、 ATTR 、 KERNEL 等,而 “ 赋值 ” 部分有一项,是 NAME 。这个规则的意思是:当系统中出现的新硬件属于 net 子系统范畴,系统对该硬件采取的动作是 “add” 这个硬件,且这个硬件的 “address” 属性信息等于 “08 : 00 : 27 : 35 : be : ff” , “dev_id” 属性等于 “0x0” 、 “type” 属性为 1 等,此时,对这个硬件在 udev 层次施行的动作是创建 /dev/eth1

通过一个简单的例子可以看出 udev 和 devfs 在命名方面的差异。如果系统中有两个 USB 打印机,一个可能被称为 /dev/usb/lp0 ,另外一个便是 /dev/usb/lp1 。但是到底哪个文件对应哪个打印机是无法确定的, lp0 、 lp1 和实际的设备没有一一对应的关系,映射关系会因设备发现的顺序、打印机本身关闭等而不确定。因此,理想的方式是两个打印机应该采用基于它们的序列号或者其他标识信息的办法来进行确定的映射, devfs 无法做到这一点, udev 却可以做到。使用如下规则:

SUBSYSTEM="usb",ATTR{serial}="HXOLL0012202323480",NAME="lp_epson",
SYMLINK+="printers/epson_stylus"

该规则中的匹配项目有 SUBSYSTEM 和 ATTR ,赋值项目为 NAME 和 SYMLINK ,它意味着当一台 USB 打印机的序列号为 “HXOLL0012202323480” 时,创建 /dev/lp_epson 文件,并同时创建一个符号链接 /dev/printers/epson_styles 。序列号为 “HXOLL0012202323480” 的 USB 打印机不管何时被插入,对应的设备名都是 /dev/lp_epson ,而 devfs 显然无法实现设备的这种固定命名。

udev 规则的写法非常灵活,在匹配部分,可以通过 “*” 、 “ ? ” 、 [a~c] 、 [1~9] 等 shell 通配符来灵活匹配多个项目。 *类似于 shell 中的 * 通配符,代替任意长度的任意字符串,?代替一个字符。此外, %k 就是 KERNEL , %n 则是设备的 KERNEL 序号(如存储设备的分区号)。

可以借助 udev 中的 udevadm info 工具查找规则文件能利用的内核信息和 sysfs 属性信息,如运行 “udevadm info-a-p/sys/devices/platform/serial8250/tty/ttyS0” 命令将得到:

udevadm info -a -p /sys/devices/platform/serial8250/tty/ttyS0
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/platform/serial8250/tty/ttyS0':
KERNEL=="ttyS0"
SUBSYSTEM=="tty"
DRIVER==""
ATTR{irq}=="4"
ATTR{line}=="0"
ATTR{port}=="0x3F8"
ATTR{type}=="0"
ATTR{flags}=="0x10000040"
ATTR{iomem_base}=="0x0"
ATTR{custom_divisor}=="0"
ATTR{iomem_reg_shift}=="0"
ATTR{uartclk}=="1843200"
ATTR{xmit_fifo_size}=="0"
ATTR{close_delay}=="50"
ATTR{closing_wait}=="3000"
ATTR{io_type}=="0"
looking at parent device '/devices/platform/serial8250':
KERNELS=="serial8250"
SUBSYSTEMS=="platform"
DRIVERS=="serial8250"
looking at parent device '/devices/platform':
KERNELS=="platform"
SUBSYSTEMS==""
DRIVERS==""

如果 /dev/ 下面的节点已经被创建,但是不知道它对应的 /sys 具体节点路径,可以采用 “udevadm info-a-p$ ( udevadm info-q path-n/dev/< 节点名 > ) ” 命令反向分析

在嵌入式系统中,也可以用 udev 的轻量级版本 mdev , mdev 集成于 busybox 中。在编译 busybox 的时候,选中 mdev相关项目即可。
Android 也没有采用 udev ,它采用的是 vold 。 vold 的机制和 udev 是一样的,理解了 udev ,也就理解了 vold 。 Android的源代码 NetlinkManager.cpp 同样是监听基于 netlink 的套接字,并解析收到的消息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值