张老汉手工酸辣粉:udev 详解(转)

来源:百度文库 编辑:偶看新闻 时间:2024/05/09 09:10:50
如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略。在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev目录下。后来,采用了devfs,一个基于内核的动态设备文件系统,他首次出现在2.3.46内核中。Mandrake,Gentoo等Linux分发版本采用了这种方式。devfs创建的设备文件是动态的。但是devfs有一些严重的限制,从2.6.13版本后移走了。目前取代他的便是文本要提到的udev--一个用户空间程序。

目前很多的Linux分发版本采纳了udev的方式,因为它在Linux设备访问,特别是那些对设备有极端需求的站点(比如需要控制上千个硬盘)和热插拔 设备(比如USB摄像头和MP3播放器)上解决了几个问题。下面我我们来看看如何管理udev设备。

实际上,对于那些为磁盘,终端设备等准备的标准配置文件而言,你不需要修改什么。但是,你需要了解udev配置来使用新的或者外来设备,如果不修改配置,这些设备可能无法访问,或者说Linux可能会采用不恰当的名字,属组或权限来创建这些设备文件。你可能也想知道如何修改RS-232串口,音频设备等文件的属组或者权限。这点在实际的Linux实施中是会遇到的。


为什么使用udev

在此之前的设备文件管理方法(静态文件和devfs)有几个缺点:

*不确定的设备映射。特别是那些动态设备,比如USB设备,设备文件到实际设备的映射并不可靠和确定。举一个例子:如果你有两个USB打印机。一个可能称为/dev/usb/lp0,另外一个便是/dev/usb/lp1。但是到底哪个是哪个并不清楚,lp0,lp1和实际的设备没有一一对应的关系,因为他可能因为发现设备的顺序,打印机本身关闭等原因而导致这种映射并不确定。理想的方式应该是:两个打印机应该采用基于他们的序列号或者其他标识信息的唯一设备文件来映射。但是静态文件和devfs都无法做到这点。

*没有足够的主/辅设备号。我们知道,每一个设备文件是有两个8位的数字:一个是主设备号 ,另外一个是辅设备号来分配的。这两个8位的数字加上设备类型(块设备或者字符设备)来唯一标识一个设备。不幸的是,关联这些身边的的数字并不足够。

*/dev目录下文件太多。一个系统采用静态设备文件关联的方式,那么这个目录下的文件必然是足够多。而同时你又不知道在你的系统上到底有那些设备文件是激活的。

*命名不够灵活。尽管devfs解决了以前的一些问题,但是它自身又带来了一些问题。其中一个就是命名不够灵活;你别想非常简单的就能修改设备文件的名 字。缺省的devfs命令机制本身也很奇怪,他需要修改大量的配置文件和程序。;

*内核内存使用,devfs特有的另外一个问题是,作为内核驱动模块,devfs需要消耗大量的内存,特别当系统上有大量的设备时(比如上面我们提到的系统一个上有好几千磁盘时)

udev的目标是想解决上面提到的这些问题,他通采用用户空间(user-space)工具来管理/dev/目录树,他和文件系统分开。知道如何改变缺省 配置能让你之大如何定制自己的系统,比如创建设备字符连接,改变设备文件属组,权限等。

udev配置文件

主要的udev配置文件是/etc/udev/udev.conf。这个文件通常很短,他可能只是包含几行#开头的注释,然后有几行选项:

udev_root="/dev/"

udev_rules="/etc/udev/rules.d/"

udev_log="err"

上面的第二行非常重要,因为他表示udev规则存储的目录,这个目录存储的是以.rules结束的文件。每一个文件处理一系列规则来帮助udev分配名字给设备文件以保证能被内核识别。
你的/etc/udev/rules.d下面可能有好几个udev规则文件,这些文件一部分是udev包安装的,另外一部分则是可能是别的硬件或者软件包生成的。比如在Fedora Core5系统上,sane-backends包就会安装60-libsane.rules文件,另外initscripts包会安装60-net.rules文件。这些规则文件的文件名通常是两个数字开头,它表示系统应用该规则的顺序。

规则文件里的规则有一系列的键/值对组成,键/值对之间用逗号(,)分割。每一个键或者是用户匹配键,或者是一个赋值键。匹配键确定规则是否被应用,而赋值键表示分配某值给该键。这些值将影响udev创建的设备文件。赋值键可以处理一个多值列表。匹配键和赋值键操作符解释见下表:


                        udev 键/值对操作符
操作符      匹配或赋值t                          解释
----------------------------------------
==             匹配               相等比较
!=             匹配              不等比较
=             赋值               分配一个特定的值给该键,他可以覆盖之前的赋值。
+=       赋值               追加特定的值给已经存在的键
:=             赋值                   分配一个特定的值给该键,后面的规则不可能覆盖它。 


这有点类似我们常见的编程语言,比如C语言。只是这里的键一次可以处理多个值。有一些键在udev规则文件里经常出现,这些键的值可以使用通配符(*,?,甚至范围,比如[0-9]),这些常用键列举如下:


常用udev键
键         含义
ACTION          一个时间活动的名字,比如add,当设备增加的时候
KERNEL          在内核里看到的设备名字,比如sd*表示任意SCSI磁盘设备
DEVPATH        内核设备录进,比如/devices/*
SUBSYSTEM        子系统名字,比如sound,net
BUS          总线的名字,比如IDE,USB
DRIVER          设备驱动的名字,比如ide-cdrom
ID            独立于内核名字的设备名字
SYSFS{ value}        sysfs属性值,他可以表示任意
ENV{ key}        环境变量,可以表示任意
PROGRAM        可执行的外部程序,如果程序返回0值,该键则认为为真(true)
RESULT          上一个PROGRAM调用返回的标准输出。
NAME          根据这个规则创建的设备文件的文件名。注意:仅仅第一行的NAME描述是有效的,后面的均忽略。
                                   如果你想使用使用两个以上的名字来访问一个设备的话,可以考虑SYMLINK键。
SYMLINK        根据规则创建的字符连接名
OWNER          设备文件的属组
GROUP          设备文件所在的组。
MODE          设备文件的权限,采用8进制
RUN          为设备而执行的程序列表
LABEL          在配置文件里为内部控制而采用的名字标签(下下面的GOTO服务)
GOTO          跳到匹配的规则(通过LABEL来标识),有点类似程序语言中的GOTO
IMPORT{ type}      导入一个文件或者一个程序执行后而生成的规则集到当前文件
WAIT_FOR_SYSFS    等待一个特定的设备文件的创建。主要是用作时序和依赖问题。
PTIONS          特定的选项: last_rule 对这类设备终端规则执行; ignore_device 忽略当前规则; ignore_remove 忽略接下来的并移走请求。
            all_partitions 为所有的磁盘分区创建设备文件。


我们给出一个列子来解释如何使用这些键。下面的例子来自Fedora Core 5系统的标准配置文件。

KERNEL=="*", OWNER="root" GROUP="root", MODE="0600"
KERNEL=="tty", NAME="%k", GROUP="tty", MODE="0666", OPTIONS="last_rule"
KERNEL=="scd[0-9]*", SYMLINK+="cdrom cdrom-%k"
KERNEL=="hd[a-z]", BUS=="ide", SYSFS{removable}=="1", SYSFS{device/media}=="cdrom", SYMLINK+="cdrom cdrom-%k"
ACTION=="add", SUBSYSTEM=="scsi_device", RUN+="/sbin/modprobe sg"

上面的例子给出了5个规则,每一个都是KERNEL或者ACTION键开头:

*第一个规则是缺省的,他匹配任意被内核识别到的设备,然后设定这些设备的属组是root,组是root,访问权限模式是0600(-rw------ -)。这也是一个安全的缺省设置保证所有的设备在默认情况下只有root可以读写。
*第二个规则也是比较典型的规则了。它匹配终端设备(tty),然后设置新的权限为0600,所在的组是tty。它也设置了一个特别的设备文件名:%K。 在这里例子里,%k代表设备的内核名字。那也就意味着内核识别出这些设备是什么名字,就创建什么样的设备文件名。

*第三行开始的KERNEL=="scd[0-9]*",表示 SCSI CD-ROM 驱动. 它创建一对设备符号连接:cdrom和cdrom-%k。

*第四行,开始的 KERNEL=="hd[a-z]", 表示ATA CDROM驱动器。这个规则创建和上面的规则相同的符号连接。ATA CDROM驱动器需要sysfs值以来区别别的ATA设备,因为SCSI CDROM可以被内核唯一识别。.

*第五行以 ACTION=="add"开始,它告诉udev增加 /sbin/modprobe sg 到命令列表,当任意SCSI设备增加到系统后,这些命令将执行。其效果就是计算机应该会增加sg内核模块来侦测新的SCSI设备。

当然,上面仅仅是一小部分例子,如果你的系统采用了udev方式,那你应该可以看到更多的规则。如果你想修改设备的权限或者创建信的符号连接,那么你需要熟读这些规则,特别是要仔细注意你修改的那些与之相关的设备。

修改你的udev配置

在修改udev配置之前,我们一定要仔细,通常的考虑是:你最好不要修改系统预置的那些规则,特别不要指定影响非常广泛的配置,比如上面例子中的第一行。 不正确的配置可能会导致严重的系统问题或者系统根本就无法这个正确的访问设备。

而我们正确的做法应该是在/etc/udev/rules.d/下创建一个信的规则文件。确定你给出的文件的后缀是rules文件名给出的数字序列应该比标准配置文件高。比如,你可以创建一个名为99-my-udev.rules的规则文件。在你的规则文件中,你可以指定任何你想修改的配置,比如,假设你修改修改floppy设备的所在组,还准备创建一个信的符号连接/dev/floppy,那你可以这么写:
KERNEL=="fd[0-9]*", GROUP="users",    SYMLINK+="floppy"


有些发行版本,比如Fedora,采用了外部脚本来修改某些特定设备的属组,组关系和权限。因此上面的改动可能并不见得生效。如果你遇到了这个问题,你就 需要跟踪和修改这个脚本来达到你的目的。或者你可以修改PROGRAM或RUN键的值来做到这点。

某些规则的修改可能需要更深的挖掘。比如,你可能想在一个设备上使用sysfs信息来唯一标识一个设备。这些信息最好通过udevinfo命令来获取。
$ udevinfo –a –p $(udevinfo –q path –n /dev/hda)

上面的命令两次使用udevinfo:一次是返回sysfs设备路径(他通常和我们看到的Linux设备文件名所在路径--/dev/hda--不同);第二次才是查询这个设备路径,结果将是非常常的syfs信息汇总。你可以找到最够的信息来唯一标志你的设备,你可以采用适当的替换udev配置文件中的SYSFS选项。下面的结果就是上面的命令输出
重要提示

...切记,在使用udev加载任何modules(内核模块)之前(无论是否是启动时自动加载),您必须在/etc/rc.conf将MOD_AUTOLOAD选项设置为yes,否则您必须手动加载这些modules。您可以修改rc.conf中的MODULES或者使用modprobe命令来手动加载您所需要的modules。另一种方法是用hwdetect--modules生成系统硬件的modules列表,然后将这个列表添加到rc.conf中让系统启动时自动加载这些modules。

基本需求

     * 内核: 2.6.15或更高版本。
     * 您将无法在fstab和bootloader设置中再使用DevFS格式的设备名称! 更多相关内容请查看DevFS to Udev。 

最近更新

     * startudev程序被取出。如果需要重新加载udev规则请使用 /etc/start_udev。
     * Udev代替了hotplug和hwdetect的功能。同时我们保存了hwdetect,并且只在 mkinitrd程序生成initrd的时候用到。
     * Udev可以同时加载多个模块,这样可以加快启动速度,然而,这样做的结果是她不能保证每次加载的顺序,所以当你使用多声卡或网卡的时候就会出现问题,这个问题下面将会再讨论。 

模块禁用列表

udev也会犯错或加载错误的模块。为了防止错误的发生,你可以使用模块禁用列表 。一旦加入该列表的模块,无论是启动时,或者时运行时(如usb硬盘等)udev都不会加载这些模块。

只需在您在 rc.conf的MODULES中对应模块前加上感叹号(!)就可禁用该模块。

例如,

MODULES=(!moduleA !moduleB)

load_modules: 有用的启动参数

如果您在内核启动参数中加入load_modules=off,那么udev会停止任何自动加载工作.如果系统出现问题时,这个功能会十分有用。如果udev加载了有问题的模块导致系统挂起或者其它严重的问题时,你可以使用这个参数来禁用自动加载,以此来防止加载有问题的模块。

已知的硬件问题

- BusLogic设备被损坏而且导致启动时死机。

这是一个内核的Bug目前还没有修正。

- PCMCIA读卡器被认为是可移除设备.

把它们加入到/etc/pmount.allow中,使用hal的pmount来读取

自动加载带来的一些问题

CPUFreq模块

我门还没有找到一个很好的方法加载不同的CPUFreq控制器,所以我们把从自动加载进程里把它去掉了。如果您需要测量CPU频率,你必须在rc.conf的MODULES队列中显式的加入合适的模块。

声音问题和一些不能自动加载的模块

一些用户跟踪发现问题出在/etc/modprobe.conf中一些旧的部分,试着去掉这些旧的部分再试试看。
多个同类型设备(网卡,声卡)每次启动的都不同

因为udev同时加载所有模块,所以一些设备可能初始化顺序不同。例如同时有两个网卡时,它们总是在eth0和eth1之间变来变去。

常用的解决办法是在您的rc.conf文件中通过修改MODULES队列来指明顺序。这个队列里的模块将在udev自动加载之前由系统加载,因此您可以控制模块在启动时加载顺序。

# 在e100之前加载8139too
MODULES=(8139too e100)

另一个解决网卡的方法是使用udev-sanctified方法为每个网卡静态命名。创建文件/etc/udev/rules.d/10-network.rules然后将不同的网卡通过MAC地址绑定到不同的名字上:

SUBSYSTEM=="net", SYSFS{address}=="aa:bb:cc:dd:ee:ff", NAME="lan0"
SUBSYSTEM=="net", SYSFS{address}=="ff:ee:dd:cc:bb:aa", NAME="wlan0"

同时,您需要注意以下内容:

     * 您可以通过下面的命令获得网卡的MAC地址:: udevinfo -a -p /sys/class/net/<你的网卡>
     * 注意在udev规则文件中使用小写的16进制MAC地址,因为udev无法识别大写的MAC地址。
     * 一些用户在使用旧的命名方式时出现问题,例如: eth0, eth1, 等等. 如果出现这个问题,试试使用 "lan"或者"wlan"之类的名字. 

注意不要忘记修改您的/dec/rc.conf和其它使用ethX命名的配置文件。

自己编译内核造成的一些已知问题

Udev无法启动

请确定您的内核版本大于或等于2.6.15。较早的内核没有udev自动装载所需要的uevent功能。

CD/DVD符号和权限错误

如果您使用2.6.15的内核的话,您需要安装ABS的uevent补丁(它从2.6.16内核中抽取了一些uevent功能)。您可以使用abs命令来 同步ABS树,然后您就可以在/var/abs/kernels/kernel26/下找到abs补丁。

Udev小窍门

自动加载usb设备

KERNEL=="sd[a-z]", NAME="%k", SYMLINK+="usb%m", GROUP="users", OPTIONS="last_rule"
ACTION=="add", KERNEL=="sd[a-z][0-9]", SYMLINK+="usb%n", GROUP="users", NAME="%k"
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mkdir -p /mnt/usb%n"
ACTION=="add", KERNEL=="sd[a-z][0-9]", PROGRAM=="/lib/udev/vol_id -t%N", RESULT=="vfat", RUN+="/bin/mount -t vfat -orw,noauto,sync,dirsync,noexec,nodev,noatime,dmask=000,fmask=111 /dev/%k/mnt/usb%n", OPTIONS="last_rule"
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mount -t auto -orw,noauto,sync,dirsync,noexec,nodev,noatime /dev/%k /mnt/usb%n",OPTIONS="last_rule"
ACTION=="remove", KERNEL=="sd[a-z][0-9]", RUN+="/bin/umount -l /mnt/usb%n"
ACTION=="remove", KERNEL=="sd[a-z][0-9]", RUN+="/bin/rmdir /mnt/usb%n", OPTIONS="last_rule"

把这些udev规则放到/etc/udev/rules.d/下任何一个文件名以.rules结尾的文件中,例如/etc/udev/rules.d/sda.rules。

如果想同时建立/media到/mnt符号连接,可以使用下面的版本:

KERNEL=="sd[a-z]", NAME="%k", SYMLINK+="usbhd-%k", GROUP="users", OPTIONS="last_rule" 
ACTION=="add", KERNEL=="sd[a-z][0-9]", SYMLINK+="usbhd-%k", GROUP="users", NAME="%k" 
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mkdir -p /media/usbhd-%k" 
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/ln -s /media/usbhd-%k /mnt/usbhd-%k" 
ACTION=="add", KERNEL=="sd[a-z][0-9]", PROGRAM=="/lib/udev/vol_id -t%N", RESULT=="vfat", RUN+="/bin/mount -t vfat -orw,noauto,sync,dirsync,noexec,nodev,noatime,dmask=000,fmask=111 /dev/%k/media/usbhd-%k", OPTIONS="last_rule" 
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mount -t auto -orw,noauto,sync,dirsync,noexec,nodev,noatime /dev/%k /media/usbhd-%k",OPTIONS="last_rule" 
ACTION=="remove", KERNEL=="sd[a-z][0-9]", RUN+="/bin/rm -f /mnt/usbhd-%k" 
ACTION=="remove", KERNEL=="sd[a-z][0-9]", RUN+="/bin/umount -l /media/usbhd-%k" 
ACTION=="remove", KERNEL=="sd[a-z][0-9]", RUN+="/bin/rmdir /media/usbhd-%k", OPTIONS="last_rule"

注意!如果你是用的其它的固定设备(例如SATA的硬盘,您可以从/etc/fstab中查看)被识别为/dev/sdX,您必须从sd[a-z]中去掉你的那个sdX。例如,如果您的SATA硬盘被是识别为/dev/sda,您就需要把所有的“sd[a-z]”替换成“sd[b-z]”。在规则文件的文件名前加上数字(如:010.udev.rules)是个很好的主意,这样udev在读取标准规则前,将会读取这个规则文件。这些规则设置后不需要修改/etc/fstab文件。请查看mount命令的参数来修改权限等特性(您可以从论坛搜索查看mount命令的参数,然后根据您的需要修改它们)。