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

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

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

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

7.9.1. 历史

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

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

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

7.9.2. Udev 实现

7.9.2.1. Sysfs

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

7.9.2.2. Udev 启动脚本

S10udev 初始化脚本负责在 Linux 启动的时候创建设备节点,该脚本将默认的/sbin/hotplug 注册为热插拔事件处理程序。之所以这样做,是因为内核不再需要调用外部的二进制文件。而 udevd 将会监听内核发起的网络连接接口事件。然后,启动脚本将/lib/udev/devices 目录里的所有静态设备节点拷贝到 /dev 目录。 这是必需的,因为在系统启动的早期,动态设备处理进程可用之前,需要一些设备,目录和符号链接, 或udevd自己需要。在/lib/udev/devices目录建立静态设备节点还为不被动态设备处理程序支持的设备提供了一个易用的工作环境。 然后启动脚本启动Udev守护进程 udevd, 它对所有接收的事件起作用。最后,启动脚本强制内核重现所有已注册设备的事件,并等待udevd 处理。

7.9.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-151/writing_udev_rules/index.html

7.9.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.9.2.5. 处理可热插拔/动态设备

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

7.9.3. 加载模块和创建设备的问题

当自动创建设备节点时,有一些可能的问题。

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

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

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

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

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

Udev 根本不加载 “封装” 驱动,如 snd-pcm-oss 和 非硬件驱动如 loop

 

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

如果 “封装” 模块仅仅增强其他一些模块提供的功能 (例如, snd-pcm-oss 通过使声卡对于OSS应用可用来增强 snd-pcm 的功能),就配置 modprobe 使其在加载了相应模块后加载其封装模块。 要这样做,就在/etc/modprobe.d/<filename>.conf 文件中,加入 “install” 行。例如 :

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

如果模块不是一个封装,而是对自己本身有用,配置S05modules 启动脚本使其在系统启动的时候加载。 要这样做,就把模块名加入到/etc/sysconfig/modules文件的一个单独的行。这对于封装模块也有用,但不是最优的。

7.9.3.3. Udev加载了一些不需要的模块

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

blacklist forte

列入黑名单的模块还可以使用 modprobe 命令手工加载。

7.9.3.4. Udev 创建了错误的设备,或创建了错误的符号链接

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

7.9.3.5. Udev 规则工作不可靠

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

7.9.3.6. Udev 没有创建设备

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

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

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

这是由于 Udev设计的问题,它以并行方式处理事件和加载模块。因此名字的顺序就会不可预测。 这将不会被“修复”。 你不应当依赖稳定的内核设备名。而是根据设备的一些稳定的属性,比如序列号或 Udev 安装的各种 *_id 工具的输出, 写自己的规则来创建符号链接。请参考 Section 7.10, “为设备创建惯用符号连接 ”Section 7.13, “配置网络脚本 ”

7.9.4. 有用的读物

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