7.4. LFS 系统的设备和模块处理

第六章里,我们安装了 Udev 软件包,在开始深入讨论它如何工作之前,我们先简要回顾一下以前处理设备的方法。

传统上一般 Linux 系统使用创建静态设备的方法,因此在 /dev 目录下创建了大量的设备节点(有时会有数千个节点),而不管对应的硬件设备实际上是否存在。这通常是由 MAKEDEV 脚本完成的,这个脚本包含许多调用 mknod 程序的命令,为这个世界上可能存在的每个设备创建相应的主设备号和次设备号。

而使用 Udev 方式的时候,只有被内核检测到的设备才为其创建设备节点。因为每次系统启动的时候都要重新创建这些设备节点,所以它们被存储在 tmpfs 文件系统(一种完全存在于内存里,不占用任何磁盘空间的文件系统)上,设备节点不需要很多磁盘空间,所占用的内存可以忽略不计。

7.4.1. 历史

2000 年 2 月的时候,2.3.46 版本的内核引入了一种称为 devfs 的文件系统,在 2.4 系列稳定版本的内核中都是可用的。尽管它存在于内核源代码中,但这种动态创建设备的方法却从未得到核心内核开发者们的全力支持。

devfs 存在的主要的问题是它处理设备检测、创建和命名的方式,其中设备节点的命名可能是最严重的问题。一般可接受的方式是,如果设备名是可配置的,那么设备命名策略应该由系统管理员决定,而不是由某些开发者强制规定。devfs 文件系统还存在竞争条件(race conditions)的问题,这是它天生的设计缺陷,不对内核做彻底的修改就无法修正这个问题。因为长久以来它都被标记为不推荐的(deprecated),并且由于缺乏维护,最终在2006 年 6 月 从内核中移除。

随着非稳定的 2.5 内核树的开发,即后来发布的 2.6 系列稳定版本内核,一种被称为 sysfs 的新虚拟文件系统诞生了。sysfs 的工作是把系统的硬件配置视图导出给用户空间的进程。由于有了这个用户空间可见的表示,代替 devfs 方案的时机就成熟了。

7.4.2. Udev 实现

7.4.2.1. Sysfs 文件系统

上面简单的提到了 sysfs 文件系统,您可能想知道 sysfs 是怎么认出系统中存在的设备以及应该使用什么设备号。对于已经编入内核的驱动程序,当被内核检测到的时候,会直接在 sysfs 中注册其对象;对于编译成模块的驱动程序,当模块载入的时候才会这样做。一旦挂载了 sysfs 文件系统(挂载到 /sys),内建的驱动程序在 sysfs 注册的数据就可以被用户空间的进程使用,并提供给 udevd 以创建设备节点。

7.4.2.2. Udev 启动脚本

S10udev 初始化脚本负责在 Linux 启动的时候创建设备节点。该脚本首先将 /sbin/hotplug 注册为热插拔事件处理程序。这样做是因为内核不再需要运行一个外部的二进制程序,相反 udevd 将会为内核传送的 uevents 信号听从 socket 网络链接。然后,这个启动脚本复制 /lib/udev/devices 里面的静态设备节点号到 /dev里面。 这是必要的,因为动态设备在处理系统启动早期的可用程序,或者是 udevd 自身需要的程序之前需要一些设备、目录和符号链接。在 /lib/udev/devices 里面创建静态的的设备节点也为设备创建了一个简单的工作区,而这些设备不被动态设备支持。之后启动脚本开启 Udev 守护进程 udevd。守护进程将会对它接收到的任何一个 uevent 有用。最终,启动脚本强制内核对任意一个已经注册的设备重试 uevent,并等待 udevd 来解决它们。

7.4.2.3. 设备节点创建

为了得到正确的主次设备号,Udev 依赖于 /sys目录下的sysfs 提供的信息。例如,/sys/class/tty/vcs/dev 包含字符串 “7:0”。这个字符串是被 udevd 用来创建主设备号是7、次设备号是 0 的设备节点。在 /dev 目录下创建的设备节点的名字和权限由 /etc/udev/rules.d/ 目录下相应的规则决定。这些以类似的形式包含在 LFS-Bootscripts 包中。如果 udevd 不能为它现在创建的设备发现一个规则,它会默认把权限设置为 660,属主设置为 root:root。Udev 规则语法的文档可以查看 /usr/share/doc/udev-130/index.html

7.4.2.4. 模块加载

被编译成模块的设备驱动可能有编译进去的别名。别名可以通过 modinfo 程序的输出来查看,通常与设备总线特有的标识有关(模块要支持)。例如,snd-fm801 驱动支持 PCI 设备,通过生产厂商 ID 0x1319 和设备 ID 0x0801,有一个别名“pci:v00001319d00000801sv*sd*bc04sc01i*”。对于大多数的设备,总线驱动通过 sysfs 导出可能会处理的设备驱动的别名。例如 /sys/bus/pci/devices/0000:00:0d.0/modalias 文件可能包含字符串“pci:v00001319d00000801sv00001319sd00001319bc04sc01i00”。为 Udev 提供的默认规则会导致 udevd 调用 /sbin/modprobe 处理热插拔事件环境变量 MODALIAS 的内容(应该与 sysfs 中的 modalias 文件的内容一样)。因此在通配符扩展之后,加载所有的别名匹配这个字符串的模块。

在这个例子中,除了 snd-fm801,荒废的(不想要的)forte 驱动会被加载(如果它可用)。查看下面的方法来避免加载不想要的模块。

内核自身能在后台加载有关网络协议、文件系统和 NLS(国际语言支持)支持的模块。

7.4.2.5. 处理可热插拔/动态设备

当您插入一个设备,例如一个 USB 接口的 MP3 播放器,内核会检测到设备连接,并产生一个热插拔事件。然后这个热插拔事件会被上面提到的 udevd 处理。

7.4.3. 创建设备的问题

自动创建设备节点的时候,存在一些已知的问题:

7.4.3.1. 内核模块没有自动加载

如果有一个总线特有的别名,并且总线驱动器导出必需的别名到 sysfs,那么 Udev 将只会加载一个模块。在其他情况下,需要安排其它的模块加载方式。在 Linux-2.6.27.4 中,Udev 为 INPUT、IDE、PCI、USB、SCSI、SERIO 和 FireWire 设备加载适当的驱动。

为了确定你请求的设备驱动 Udev 是否支持,可以以模块的名字为参数运行 modinfo。现在把设备目录设置到到 /sys/bus ,检测那里是否有一个 modalias 文件。

如果 modalias 文件存在于 sysfs 中,驱动程序支持设备并且可以直接通信,但是没有别名,这是驱动中的一个 bug。不要利用 Udev 的帮助加载驱动,希望在以后这个问题会被修正。

如果 /sys/bus 的相应目录内没有 modalias 文件,这就意为着内核开发者还没有添加对这种总线类型的模块别名的支持。在 Linux-2.6.27.4 中,ISA 总线就是这种情况。希望在下一个内核版本中会修正这个问题。

Udev 根本不会加载像 snd-pcm-oss 的“包装”驱动和像 loop 的非硬件驱动。

7.4.3.2. 内核模块没有自动加载,并且 Udev 也不加载

如果“包装”模块只是增强其它模块提供的功能(例如 snd-pcm-oss 增强了 snd-pcm,它使声卡对于 OSS 程序可用),可以配置 modprobe 使其在加载了相应模块后加载其包装模块。着可以通过在/etc/modprobe.conf 中添加一行 “install” 来实现,例如:


install snd-pcm /sbin/modprobe -i snd-pcm ; \

    /sbin/modprobe snd-pcm-oss ; true

如果模块不是一个包装,而是对自己本身有用,配置 S05modules 启动脚本使其在系统启动的时候加载。要完成此任务,可以在 /etc/sysconfig/modules 文件的相应行上添加模块的名字。包装模块也可以按照这种方式处理,但并不是最适合的。

7.4.3.3. Udev 加载了不需要的模块

在下面例子中,对于 forte 模块,可以不编译它或者把它列入文件 /etc/modprobe.conf 的黑名单里。


blacklist forte

列入黑名单的模块仍然能够通过 modprobe 命令手工加载。

7.4.3.4. Udev 错误地创建了设备或创建了错误的符号链接

如果规则匹配的不是预想的设备,那么这种情况就会经常发生。例如,一个写的很糟糕的规则通过计算机提供商匹配到一个 SCSI 硬盘(期望的)和一个一般的 SCSI 设备(错误的)。找出这些不合格的规则,在命令 udevadm info 的帮助下更正它们。

7.4.3.5. Udev 规则不可靠

这或许是前面问题的一个表现。如果不是,并且你的规则使用 sysfs 属性,那么这可能是一个将在下一个版本的内核中被修正的内核计时问题。现在,你可以通过创建一个等待使用 sysfs 属性的规则,把它添加到 /etc/udev/rules.d/10-wait_for_sysfs.rules 文件中(如果这个文件不存在,请先创建它)。如果你做了,并且它是有效的,请通知LFS开发邮件列表。

7.4.3.6. Udev 没有创建设备

后面的文本假设驱动已经被静态地编译进了内核或者是已经作为模块被加载,你已经确认了 Udev 没有创建错误的设备。

如果内核驱动没有将其数据导出到 sysfs,Udev 就没有足够信息来创建设备节点。这个问题在内核源代码树之外的第三方驱动程序上尤为常见。用恰当的主/次设备号在 /lib/udev/devices 中创建一个静态的设备节点(参考内核文档里的 devices.txt 或者第三驱动厂商提供的文档)。静态的设备节点将被 S10udev 启动脚本拷贝到 /dev 目录中。

7.4.3.7. 重启后设备名字顺序变化

这是由于 Udev 设计的问题,它以并行方式处理热插拔事件和加载模块,因此名字的顺序就会不可预测。这将不会被“修正”。不要期望内核设备的名字是稳定的,而是应当根据设备稳定的属性(比如,序列号或者 Udev 安装的各种 *_id 工具的输出)写自己的规则来创建符号链接。例子请参见第 7.12 节 “为设备创建惯用的符号链接”第 7.13 节 “配置网络脚本”

7.4.4. 有用的读物

一些有用的补充文档可以在下列网站得到:


Host by Alair