9.11. 热插拔: 热插拔
9.11.1. 介绍
hotplug 次系统核心,以动态方式加载适当的驱动程且 (在 udevd
的协助下)添加对应的设备文件,处理加入与移除设备的作业。当代的硬件与虚拟化,几乎每个对象都是热插拔:从常见的 USB/PCMCIA/IEEE 1394 周边到 SATA 硬盘,以及 CPU 与内存。
核心内的数据库有每个设备的 ID 及其驱动程序。在启动阶段加载此数据库,侦测各接口的周边设备,并在运行中侦测热插入的设备。接收到插入的设备后,送出消息给 udevd
,让其添加对应的条目于 /dev/
内。
9.11.2. 命名问题
在热插拔连接出现之前,很容易给设备赋予一个固定的名字。可以通过设备在总线上的位置简单命名。但是,如果设备能够在总线上来去自如,这就行不通了。典型的例子是数码相机和U 盘,两者都表现为磁盘驱动器。前者可能链接为 /dev/sdb
,后者可能是 /dev/sdc
(假设/dev/sda
代表计算机自身的硬盘)。设备名是不固定的;它取决于设备连接的顺序。
另外,越来越多的驱动使用动态值作为设备的主、次设备号,这样就更不可能给设备赋予静态入口,而重启之后这些基本的特性也随之变化。
udev 正是为了解决该问题而创立的。
9.11.3. udev 如何工作
当 udev 被核心告知有个新的设备,它参考 /sys/
里对应的条目,搜集该设备的信息,尤其是那些足辨别的独特信息 (网卡的 MAC 地址、某些 USB 设备的序号)。
有了这些信息之后,然后 udev 会查阅/etc/udev/rules.d/
和 /lib/udev/rules.d/
中包含的所有规则。在这个过程中,它会决定如何命名设备,创建什么样的符号连接(赋予设备另外的名字),执行什么命令。查询所有的文件,顺序(除非文件中使用“GOTO”指令)检查所有的规则。这样,有可能一个事件(event)对应多个规则。
规则文件的语法很简单:每行包含选择规则和变量赋值。前者用于需要对那些事件作出相应,后者定义采取何种行动。它们通过逗号分隔,用运算符区分选择规则(使用比较运算符,例如==
or !=
)或赋值指令(使用 =
, +=
或 :=
运算符)。
比较运算符用于如下变量:
内核
:内核赋予设备的名字;行动
:与事件相对应的行动(“add”当设备被添加,“remove”当设备被移除);DEVPATH
:设备在/sys/
记录中的路径;SUBSYSTEM
:产生请求的内核子系统(有很多这样的子系统,少数的例子是“usb”,“usb”,“net”,“firmware”,等);ATTR{*属性*}
:属性 文件的内容在设备的/sys/*$devpath*/
文件夹内。可在此找到 MAC 地址及其他辨识用的总线;KERNELS
,SUBSYSTEMS
和ATTRS{*attributes*}
是用来匹配当前设备父设备的选项变量;PROGRAM
:指明要运行的测试程序(真则返回0)。程序的输出内容会被储存以便RESULT
测试重用;RESULT
:对最后一次调用PROGRAM
产生的结果进行检查。
右操作数可以使用模式表达式来同时匹配几个值。比如, *
匹配任何字符串(甚至是空字符串); ?
匹配任何一个字符, []
匹配方括号中间的字符集(如果首字符是惊叹号标示求反集,连续的字符集可表示为如 a-z
)。
关于赋值操作符, =
用来赋值(并取代当前值);如果用在列表上,列表被清空并只包含赋予的新值。 :=
做同样工作,但是它会阻止随后对该变量的更改。至于 +=
,则是给列表添加新项目。如下的变量可以被更改:
NAME
:将在/dev/
中创建的设备文件名。只有第一次赋值起作用;其它的会被忽略;SYMLINK
:指向同一个设备的符号列表清单;OWNER
,GROUP
和MODE
指示拥有该设备的用户和组,还有相关的访问许可;RUN
:响应事件时执行的程序清单。
赋予这些变量的值可以使用一系列的替代表示:
$kernel
或者%k
:等价于KERNEL
;$number
或者%n
:设备的顺序号码,例如,对sda3
,它就是“3”;$devpath
或%p
:等价于DEVPATH
;$attr{*attribute*}
或%s{*attribute*}
: 等价于ATTRS{*attribute*}
;$major
或%M
:设备的内核主设备号;$minor
或%m
:内核次设备号码;$result
或%c
:PROGRAM
设定的最后一个程序输出的字符串;最后,
%%
和$$
相应代表百分号和美元符号。
以上的清单仍不完备 (只包括最重要的参数),详细的数据在 udev(7) 手册页面。
9.11.4. 一个具体例子
我们来考虑一个U盘并给它指派固定名字的例子。首先,必须要找到能唯一识别它的元素。可以插入并运行 udevadm info -a -n /dev/sdc
( 用指派给U盘的名字代替/dev/sdc )。
#
可以通过检测设备变量,还有父设备变量创建新的规则。上面的例子运行我们创建两个这样的规则:
- KERNEL=="sd?", SUBSYSTEM=="block", ATTRS{serial}=="07032998B60AB777", SYMLINK+="usb_key/disk"
- KERNEL=="sd?[0-9]", SUBSYSTEM=="block", ATTRS{serial}=="07032998B60AB777", SYMLINK+="usb_key/part%n"
这些规则在文件中设定,例如该文件名为 /etc/udev/rules.d/010_local.rules
,就可以移除和重新插入U盘了。可以看到文件/dev/usb_key/disk
代表和U盘相关联的磁盘,/dev/usb_key/part1
是它的第一个扇区。
进阶 调试udev配置
如同其他的后台进程,udevd
把日志保存在 /var/log/daemon.log
。但是缺省是不啰唆的,且不足以了解发生的事。udevadm control --log-priority=info
命令增加其详细的内容并可解决此问题。udevadm control --log-priority=err
则回到缺省的层次。