将可移动设备连入系统时,系统的后台中会依次发生如下事件:
内核检测到新硬件插入,然后分别通知
hotplug
和
udev
。前者用来装入相应的内核模块
(
如
usbstorage)
,而后者用来在
/dev
中创建相应的设备节点
(
如
/dev/sda1)
。
udev
创建了相应的设备节点之后,会将这一消息通知
hal
的守护程序
(hald)
。当然
udev
还得保证新创建的设备节点可以被普通用户访问。
l
hotplug
装入了相应的内核模块之后,会把这一消息通知给
hald
。
l
hald
在受到
hotplug
和
udev
发出的消息之后,认为新硬件已经正式被系统认可了。此时它会通过一系列精心编写的规则文件(就是传说中的
xxx-policy.fdi
),把发现新硬件的消息通过
dbus
发送出去,同时还会调用
update-fstab
或
fstab-sync
来更新
/etc/fstab
,为相应的设备节点创建适合的挂载点。
hotplug
包和内核里的
hotplug
模块不是一回事,
2.6
内核里的
pci_hotplug.ko
是一个内核模块,而
hotplug
包是用来处理内核产生的
hotplug
事件。这个软件包还在引导时检测现存的硬件并在运行的内核中加载相关模块。
不但有热插拔,还有冷插拔(
cold pluging
)。热插拔在内核启动之后发生,而
“cold pluging”
发生在内核启动的过程中。
/etc/hotplug/*.rc
这些脚本用于冷插拔
(
检测和激活在系统启动时已经存在的硬件
)
。它们被
hotplug
初始化脚本调用。
*.rc
脚本会尝试恢复系统引导时丢失的热插拔事件,举例来说,内核没有挂载根文件系统。
/etc/hotplug/*.agent
这些脚本将被
hotplug
调用以响应内核产生的各种不同的热插拔事件,导致插入相应的内核模块和调用用户预定义的脚本。
/sbin/hotplug
内核默认情况下将在内核态的某些事情发生变化时
(
如硬件的插入和拔出
)
调用此脚本。
发送热插拔事件的子系统(
subsystem
)包括总线驱动(
USB
、
PCI
等)和一些设备的抽象层(网络接口、磁盘分区等)。它们通过
/sbin/hotplug
的第一个参数来识别。
对于设备驱动来说,需要在代码里设置
MODULE_DEVICE_TABLE
,指向驱动程序感兴趣的设备的设备
ID
列表。
l
udev
在
2.6
内核里,使用了
udev
来取代
hotplug
。据
udev
的作者
Greg K.H
说,之所以废弃了
hotplug
原因是
sysfs
的出现,这个东西会产生非常多的
hotplug
事件,远远超过了
2.4
的内核(只要实现了了
kobject
模型的设备驱动都回产生该事件)。所以
hotplug
变得复杂,而且因为
hotplug
都是
bash
所写,所以开始变得没有效率。于是出现了一个名叫
hotplug-ng
的项目,就是为了解决这个过于复杂以及缺乏效率的问题,
ng
应该是
next generation
的意思。但这个项目目前为止还不能胜任角色,所以
udev
挺身而出,充当了救火队员。
2.6.15
之后,
/proc/sys/kernel/hotplug
会成空的,因为内核通知用户空间的接口变成了
netlink
,所以最新的
udev
也采用了
netlink
接口去写,废弃了
/sbin/hotplug
或者
/sbin/udevsend
。
udev
在
2.6.15
以后的内核上可以直接通过
netlink
接听设备事件,
sysfs
提供了
uevent
文件,对该文件的
“
写
”
可以送出设备事件!
udev
完全在用户态
(userspace)
工作,利用设备加入或移除时内核所发送的
hotplug
事件
(event)
来工作。关于设备的详细信息是由内核输出
(export)
到位于
/sys
的
sysfs
文件系统的。所有的设备命名策略、权限控制和事件处理都是在用户态下完成的。与此相反,
devfs
是作为内核的一部分工作的。
传统上一般
Linux
系统使用创建静态设备的方法,因此在
/dev
目录下创建了大量的设备节点
(
有时会有数千个节点
)
,而不管对应的硬件设备实际上是否存在。这通常是由
MAKEDEV
脚本完成的,这个脚本包含许多调用
mknod
程序的命令,为这个世界上可能存在的每个设备创建相应的主设备号和次设备号。而使用
udev
方式的时候,只有被内核检测到的设备才为其创建设备节点。因为每次系统启动的时候都要重新创建这些设备节点,所以它们被存储在
tmpfs
文件系统上,设备节点不需要很多磁盘空间,所占用的内存可以忽略不计。
S03udev
初始化脚本负责在
Linux
启动的时候创建设备节点,该脚本首先将
/sbin/udevsend
注册为热插拔事件处理程序。热插拔事件
(
随后将讨论
)
本不应该在这个阶段发生,注册
udev
只是为了以防万一。然后
udevstart
遍历
/sys
文件系统,并在
/dev
目录下创建符合描述的设备。例如,
/sys/class/tty/vcs/dev
里含有
"7:0"
字符串,
udevstart
就根据这个字符串创建主设备号为
7
、次设备号为
0
的
/dev/vcs
设备。
udevstart
创建的每个设备的名字和权限由
/etc/udev/rules.d/
目录下的文件指定的规则来设置。如果
udev
找不到所创建设备的权限文件,就将其权限设置为缺省的
660
,所有者为
root:root
。上面的步骤完成后,那些已经存在并且已经内建驱动的设备就可以使用了。
对于以模块驱动的设备,当内核检测到一个新设备连接时,内核会产生一个热插拔事件,并在
/proc/sys/kernel/hotplug
文件里查找处理设备连接的用户空间程序(新的内核通知接口改变,
/proc/sys/kernel/hotplug
为空了)。
udev
初始化脚本将
udevsend
注册为该处理程序。当产生热插拔事件的时候,内核让
udev
在
/sys
文件系统里检测与新设备的有关信息,并为新设备在
/dev
里创建项目。
所有在
sysfs
中显示的设备都可以由
udev
来创建节点。如果内核中增加了其它设备的支持,
udev
也就自动地可以为它们工作了。
大多数
Linux
发行版通过
/etc/modules.conf
配置文件来处理模块加载,对某个设备节点的访问导致相应的内核模块被加载。对
udev
这个方法就行不通,因为在模块加载前,设备节点根本不存在。
Linux
的设计是在设备被发现的时候加载模块,而不是当它被访问的时候。通过在
/etc/sysconfig/modules
文件里添加模块名,就可以在系统启动的时候加载这些模块,这样
udev
就可以检测到设备,并创建相应的设备节点了。
如何写
udev
规则。通过
udevinfo
程序来找到那些可以作为规则文件里的匹配项的项目。分为两种情况:第一种情况是,当你把设备插入系统后,系统为设备产生了设备名(如
/dev/sda
)。那样的话,你先用
udevinfo -q path -n /dev/sda
,命令会产生一个该设备名对应的在
sysfs
下的路径,如
/block/sda
。然后,你再用
udevinfo -a -p /sys/block/sda
,这个命令会显示一堆信息,信息分成很多块。这些信息实际来自于操作系统维护的
sysfs
链表,不同的块对应不同的路径。你就可以用这些信息来作为
udev
规则文件中的匹配项。但需要注意的是,同一个规则只能使用同一块中显示的信息,不能跨块书写规则;第二种情况是,不知道系统产生的设备名,那就只有到
/sys
目录下去逐个目录查找了,反复用
udevinfo -a -p /sys/path...
这个命令看信息,如果对应的信息是这个设备的,那就恭喜你。否则就再换个目录。当然,在这种情况下,成功的可能性比较小。
l
HAL
HAL
位于设备驱动程序和应用程序之间。
l
D-BUS
D-BUS
是一个大有前途的消息总线和活动系统,正开始深入地渗透到
Linux
桌面之中。
D-BUS
本质上是进程间通信(
inter-process communication
)(
IPC
)的一个实现,设计用于桌面应用程序和
OS
通信。
典型的
D-BUS
设置将由几个总线构成。一个持久的系统总线(
system bus
),它在引导时就会启动。这个总线由操作系统和后台进程使用,安全性非常好,以使得任意的应用程序不能欺骗系统事件。还将有很多会话总线(
session buses
),这些总线当用户登录后启动,属于那个用户私有。
一个更为有趣但很不实用的例子是
Jamboree
和
Ringaling
的结合。
Jamboree
是一个简单的音乐播放器,它具有
D-BUS
接口,以使得它可以被告知播放、到下一首歌、改变音量等等。
Ringaling
是一个小程序,它打开
/dev/ttyS0
(一个串行端口)并观察接收到的内容。当
Ringaling
发现文本
“RING”
时,就通过
D-BUS
告知
Jamboree
减小音量。最终的结果是,如果您的计算机上插入了一个调制解调器,而且电话铃响,则音乐音量就会为您减小。
这正是计算机所追求的
!
l
一些查看硬件信息的工具
lspci
列出所有
PCI
设备。有两个参数是比较常用,
-b
和
-v
,
lspci
也会把
usb
接口列出来。
lshal
列出系统硬件设备。
Usbmodules
列出可用于已插入
usb
设备的驱动模块。
l
卷管理器会监听
dbus
中发现新硬件的消息。根据所插入的硬件
(
区分
U
盘和数码相机等
)
不同,卷管理器会先将相应的设备节点挂载到
hald
创建的挂载点上,然后再打开不同的应用程序。
当然,如果是在
CDROM
中插入光盘,过程可能比较简单。因为
CDROM
本身就是一个固定的硬件,无需
hotplug
和
udev
的协助:
l
hald
会自己监视
CDROM
,并且将光盘托架开合的消息通过
dbus
发出去。
l
卷管理器负责检查
CDROM
中的盘片内容,进行挂载,并调用合适的应用程序。
要注意,
hald
的工作是从上游得到硬件就绪的消息,然后将这个消息转发到
dbus
中。尽管它会调用程序来更新
fstab
,但实际上它自己并不执行挂载的工作。
下面是上面的过程中涉及的模块和工具:
l
hotplug