使用 uevent
在 sysfs 下的很多 kobject 下都有 uevent 属性,它主要用于内核与 udev (自动设备发现程序)之间的一个通信接口;从 udev 本身与内核的通信接口 netlink 协议套接字来说,它并不需要知道设备的 uevent 属性文件,但多了 uevent 这样一个接口,可用于 udevmonitor 通过内核向 udevd (udev 后台程序)发送消息,也可用于检查设备本身所支持的 netlink 消息上的环境变量,这个特性一般用于开发人员调试 udev 规则文件, udevtrigger 这个调试工具本身就是以写各设备的 uevent 属性文件实现的。
这些 uevent 属性文件一般都是可写的,其中 /sys/devices/ 树下的很多 uevent 属性在较新内核下还支持可读:
# find /sys/ -type f -name uevent -ls 11 0 -rw-r--r-- 1 root root 4096 12月 12 21:10 /sys/devices/platform/uevent 1471 0 -rw-r--r-- 1 root root 4096 12月 12 21:10 /sys/devices/platform/pcspkr/uevent 3075 0 -rw-r--r-- 1 root root 4096 12月 12 21:10 /sys/devices/platform/vesafb.0/uevent 3915 0 -rw-r--r-- 1 root root 4096 12月 12 21:10 /sys/devices/platform/serial8250/uevent 3941 0 -rw-r--r-- 1 root root 4096 12月 12 21:10 /sys/devices/platform/serial8250/tty/ttyS2/uevent 3950 0 -rw-r--r-- 1 root root 4096 12月 12 21:10 /sys/devices/platform/serial8250/tty/ttyS3/uevent 5204 0 -rw-r--r-- 1 root root 4096 12月 12 21:10 /sys/devices/platform/i8042/uevent [...] 912 0 -rw-r--r-- 1 root root 4096 12月 12 21:17 /sys/devices/pci0000:00/0000:00:02.5/uevent [...] |
上面截取的最后一个是 SCSI 硬盘控制器设备的 uevent 属性文件,这些 /devices/ 属性文件都支持写入,当前支持写入的参数有 "add","remove","change","move","online","offline"。如,写入 "add",这样可以向 udevd 发送一条 netlink 消息,让它再重新一遍相关的 udev 规则文件;这个功能对开发人员调试 udev 规则文件很有用。
# echo add > /sys/devices/pci0000:00/0000:00:02.5/uevent |
使用驱动(PCI)的 sysfs 属性文件, bind, unbind 和 new_id
在设备驱动 /sys/bus/*/driver/... 下可以看到很多驱动都有 bind, unbind, new_id 这三个属性,
# find /sys/bus/*/drivers/ -name bind -ls
... |
每一个设备驱动程序在程序内以某种方式注明了可用于哪些硬件,如所有的 PCI 驱动都使用 MODULE_DEVICE_TABLE 声明了所能驱动的 PCI 硬件的 PCI 设备号。但驱动程序不能预知未来,未来生产的新的硬件有可能兼容现有硬件的工作方式,就还可以使用现有硬件驱动程序来工作。在 bind 和 unbind 发明以前,这种情况除了修改 PCI 设备驱动程序的 DEVICE_TABLE 段落,重新编译驱动程序,以外别无他法,在 2.6 内核上添加了 bind 和 unbind 之后可以在不重新编译的情况下对设备和驱动之间进行手工方式地绑定。
而且对于有些硬件设备可以有多份驱动可用,但任何具体时刻只能有一个驱动程序来驱动这个硬件,这时可以使用 bind/unbind 来强制使用和不使用哪一个驱动程序;(注意关于多种驱动程序的选择,更好的管理方法是使用 modprobe.conf 配置文件,需要重启才生效,而 bind/unbind 提供的是一种临时的无需重启立即生效的途径;)
使用它们可以强制绑定某个设备使用或强制不使用某个驱动程序,操作方法就是通过 bind 和 unbind 接口。
#find /sys/-type f ( -name bind -or -name unbind -or -name new_id )
-ls
69 0 -rw-r--r-- 1 root root 4096 12月 12 22:12
/sys/devices/virtual/vtconsole/vtcon0/bind
3072 0 --w------- 1 root root 4096 12月 12 22:15
/sys/bus/platform/drivers/vesafb/unbind
[...]
6489 0 --w------- 1 root root 4096 12月 12 22:09
/sys/bus/pci/drivers/8139too/unbind
6490 0 --w------- 1 root root 4096 12月 12 22:09
/sys/bus/pci/drivers/8139too/bind
6491 0 --w------- 1 root root 4096 12月 12 22:15
/sys/bus/pci/drivers/8139too/new_id |
这个结果中特别提到了 8139too 这份驱动程序的这三个属性文件,
# find /sys/bus/pci/drivers/8139too/ -ls
6435 0 drwxr-xr-x 2 root root 0 12月 12 22:08
/sys/bus/pci/drivers/8139too/
6436 0 lrwxrwxrwx 1 root root 0 12月 12 22:08
/sys/bus/pci/drivers/8139too/0000:00:0e.0 ->
../../../../devices/pci0000:00/0000:00:0e.0
6485 0 lrwxrwxrwx 1 root root 0 12月 12 22:08
/sys/bus/pci/drivers/8139too/module -> ../../../../module/8139too
6488 0 --w------- 1 root root 4096 12月 12 22:08
/sys/bus/pci/drivers/8139too/uevent
6489 0 --w------- 1 root root 4096 12月 12 22:08
/sys/bus/pci/drivers/8139too/unbind
6490 0 --w------- 1 root root 4096 12月 12 22:08
/sys/bus/pci/drivers/8139too/bind
6491 0 --w------- 1 root root 4096 12月 12 22:08
/sys/bus/pci/drivers/8139too/new_id
# echo 0000:00:0e.0 > /sys/bus/pci/drivers/8139too/unbind
-bash: echo: write error: 没有那个设备
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc
pfifo_fast state
UNKNOWN qlen 1000
link/ether 00:14:2a:d1:16:72 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.102/24 brd 192.168.1.255 scope global eth0
3: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN
link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
# echo -n 0000:00:0e.0 > /sys/bus/pci/drivers/8139too/unbind
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
3: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN
link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
# echo -n 0000:00:0e.0 > /sys/bus/pci/drivers/8139too/bind
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
3: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN
link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
4: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state
DOWN qlen 1000
link/ether 00:14:2a:d1:16:72 brd ff:ff:ff:ff:ff:ff |
这一段操作过程演示了如何对 PCI 设备 "0000:00:0e.0" 强制取消绑定 "8139too" 驱动和强制绑定 "8139too" 驱动:
对 unbind 属性写入总线号码(bus_id)即是强制取消绑定;
对 bind 属性写入总线号码(bus_id)即是强制绑定;
注意,它要求的写入的是总线号码,对应于PCI设备的总线号码是按照 "domain(4位):bus(2位):slot(2位):function号(不限)" 的方式组织,是可以从其设备 kobject 节点上找到,而其它类型的总线有各自不同的规则;
请特别注意: 在这一个例子中, "echo 0000:00:0e.0 > /sys/bus/pci/drivers/8139too/unbind" 这第一个写入命令以 "No such device" 为错误退出,而后续的 "echo -n" 命令则可以成功。这是因为内核在对总线号码进行匹配时过于严格了,通常的 "echo" 命令写入一个字符串会以一个换行符结束输出,内核所接收到的是带有这个换行符的 bus_id 字符串,将它与内核数据结构中的真正的 bus_id 字符串相比较,当然不能找到;所幸的是,这个问题在最新的 2.6.28 开发中的内核上已已经解决,它将这个比较函数改为一个特殊实现的字符串比较,自动忽略结尾处的换行符,在 2.6.28-rc6 内核上测试,不带"-n"参数的 echo 命令已经可以写入成功。
而 new_id 属性文件也可以以另一种途径解决新的设备号问题:它是一个只写的驱动属性,可用于向其中写新的设备号。它支持写入 2至7个十六进制整形参数,分别代表 vendor, device, subvendor, subdevice, class, class_mask, driver_data 最少为 2个是因为一个 PCI设备主要以厂商号(vendor)和设备号(device)所唯一标定,其它 5个参数如果不输入则缺省值为 PCI_ANY_ID(0xffff)。
5441 0 --w------- 1 root root 4096 12月 14 18:15 /sys/bus/pci/drivers/8139too/new_id |
从 8139too 驱动上可以看到它当前所静态支持的设备号码列表,其中包括当前系统中的设备 10ec:8139, 假设未来有一款 8140 设备也满足 8139 设备的硬件通讯协议,于是可以使用 8139too 驱动程序来驱动它,操作如下
# echo '10ec 8140' > /sys/bus/pci/drivers/8139too/new_id |
这在不更新驱动程序的情况下调试设备很有用处。