Udev设备管理
一、udev特点
- udev是一种工具,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。
-
使用udev后,在/dev/目录下就只包含系统中真正存在的设备
-
与设备主/从编号无关
-
提供持续的设备命名
-
Udev是硬件平台无关的,属于user space的进程,它脱离驱动层的关联而建立在操作系统之上。
-
遵循linux Standard Base (LSB)设备命名方法,但也可以自定义命名。
-
非常轻量级
使用udev后,在/dev/目录下就只包含系统中真正存在的设备Udev只支持linux-2.6以上的内核,因为udev严重依赖于sysfs文件系统提供的信息,而sysfs文件系统只在linux-2.6以上的内核才有。Udev运行在用户模式中是一个用户程序,而devfs运行在内核是一个内核模块。Udev能够实现所有devfs实现功能。Udev通过对内核产生的设备名增加别名的方式来达到不管设备连接顺序而维持一个统一的设备名的目的。Udev是用户模式程序,不会更改内核的行为。内核依然会我行我素地产生设备名,但udev可以根据设备的期它信息如总线(BUS),生产商(vendor)等不同来区分不同的设备,并产生设备文件。
二、udev原理和结构
1、当udev从kernel收到设备add/remove事件后,udev将解析:
- /etc/udev/rules.d/目录中的用户自定义规则文件。
- 使用自定义规则输出(可选)。
- 从/sys中查询相应的信息。
2、据解析的信息,udev汇集了:
- 处理设备命名
- 确定将创建什么设备文件(device file)或符号链接(symlink)
- 确定如何设备文件属性。
- 确定后续操作
传统上Linux系统使用创建静态设备文件的方法来管理系统硬件,因此在/dev目录下创建了大量的设备文件(有时会有数千个文件),而不管对应的硬件设备实际上是否存在。这些操作通常是由MAKEDEV脚本完成的,这个脚本包含许多调用mknod程序的命令,为这个世界上可能存在的每个设备创建相应的主设备号和次设备号。而使用udev方式的时候,只有被内核检测到的设备才为其创建设备文件。因为每次系统启动的时候都要重新创建这些设备节点,所以它们被存储在tmpfs文件系统(一种完全存在于内存里,不占用任何磁盘空间的文件系统)上,设备文件不需要很多磁盘空间,所占用的内存可以忽略不计。
devfs存在的主要的问题是它处理设备检测、创建和命名的方式,其中设备节点的命名可能是最严重的问题。一般可接受的方式是,如果设备名是可配置的,那么设备命名策略应该由系统管理员决定,而不是由某些开发者强制规定。devfs文件系统还存在竞争条件(raceconditions)的问题,这是它天生的设计缺陷,不对内核做彻底的修改就无法修正这个问题。因为近来缺乏维护,它已经被标记为deprecated(反对的)。
对于已经编入内核的驱动程序,当被内核检测到的时候,会直接在sysfs中注册其对象;对于编译成模块的驱动程序,当模块载入的时候才会这样做。一旦挂载了sysfs文件系统(挂载到/sys), 内建的驱动程序在sysfs注册的数据就可以被用户空间的进程使用,并提供给udev以创建设备节点。udev通过系统调用函数libsysfs得到设备的相应信息比如本地名、设备主/从号等等。
udev初始化脚本负责在Linux启动的时候创建设备节点(RedHatLinux中该脚本为/sbin/start_udev),该脚本首先将注册一个热插拔事件处理程序(Redhat Linux中该程序为/sbin/udevd)。热插拔事件本不应该在这个阶段发生,注册udev只是为了以防万一。然后udev初始化脚本遍历/sys文件系统,并在/dev目录下创建符合描述的设备。例如/sys/class/tty/tty1/dev里含有“4:1″字符串,udev初始化脚本就根据这个字符串创建主设备号为4、次设备号为1的/dev/tty1设备。创建的每个设备的名字和权限由/etc/udev/rules.d/目录下的文件指定的规则来设置,这些文件以类似于LFS启动脚本风格的编号。如果udev找不到所创建设备的权限文件,就将其权限设置为缺省的660,所有者为root:root。在RedHat Linux系统中略有不同,具体请参看/sbin/start_udev脚本。
三、配置udev
/etc/udev/udev.conf配置文件
1、udev_root 创建设备文件的目录位置(默认为/dev).
2、Udev_db-udev信息存放,硬件数据库文件 /dev/.udev/db
3、Udev_permissions:udev权限文件的名字或者所在目录 /etc/udev/permiss
4、udev_rules创建设备文件所需规则的目录位置(默认为/etc/udev/rules.d).
5、udev_log syslog(3)优先级(默认为err,可选为info各 debug)
允许过程中可以使用udevcontrol log_priority=<value>的方式修改
6、在udev读取过程中将忽略配置文件中以#号开头的行。
7、全局配置文件是/dev/udev/udev.conf
udev_root── 定义在那个目录中创建所需的设备文件,默认位置为/dev目录。
udev_rules── 定义从那个配置文件或配置目录中读取udev规则,默认位置为/etc/udev/rules.d目录
udev_log── 定义在传递log到syslog系统日志时的优先级,
在调试udev的时候也可以run-time的使用 udevcontrol命令修改这个优先级。
默认优先级为err,其它可选优先级为info和debug。
四、udev规则
1、文件名位置及命名方法
1* 默认放置在/etc/udev/rules.d目录下
2* 临时规则/dev/.udev/rules.d/
3* 命名为<rule_name>.rules
2、规则结构
ACTION==”add”,SUBSYSTEM==”mmc”, RUN+=”modprobe mmc_block”
ACTION==”add”,KERNEL==”sda”, RUN+=”/bin/raw/dev/raw/raw1 %N”
3、规则文件将在第一次预读后缓冲
当规则文件被修改后时间戳的更新会强制udev从读规则文件,如果想强制重读可以使用touch命令更新时间戳。
4、Udev不会因为找到匹配的规则而停止运行,它会继续寻找并尝试应用每一条它所能识别的规则。
五、udev关键字
1、运算符
==比较以判断相等
!=比较以判断不符
2、关键字示例
1* ACTION==”add”
2* KERNEL==”sd[a-z]1”
3* BUS==”scsi”
4* DRIVER!=”ide-cdrom”
5* SYSFS{prod_id1}==”IBM”
6* PROGRAM==”check-cdrom.sh%k CD-R”
7* SYSLINK+=”cdwriter cdwriter-%k cdrw cdrw-%k”
3、udev关键字文档
1* man 7 udev
2* /usr/share/doc/udev-095/writing_udev_rules/index.html
3* 常用关键字
关键字 |
说明 |
ACTION |
匹配事件行动的名字(add或remove),多数情况下在其后跟上添加或删除设备文件的程序 |
KERNEL |
和设备的内核名字匹配。比如sd*代表任意scsi设备 |
DEVPATH |
和设备路径匹配 |
SUBSYSTEM |
和设备所属的子系统匹配。如:sound,net,usb |
BUS |
匹配总线的名字,如:SCSI,IDE,USB |
DRIVER |
与设备的驱动程序匹配。如:ide-cdrom |
ID |
匹配独立于内核名字的设备名 |
SYSFS{filename} |
和设备路基匹配的sysfs属性值 |
ENV{key} |
匹配于环境变量依赖的值 |
PROGRAM |
执行外部程序,如果程序运行正确没有返回代码0,则此键为真。整行规则语法对此关键字都是可利用的。程序的输出将定向到stdout,它将传递给RESULT关键字 |
RESULT |
Match the returned string of the last PROGRAM call. This key can be used in the same or in any later rule after a PROGRAM call. |
4* 大多领域支持的模式匹配形式
通配符 |
说明 |
* |
匹配零个或更多个字符 |
? |
匹配任何一个字符 |
[] |
匹配中括号内声明的任意一个字符 |
[a-z] |
匹配中括号内声明a-z中的任意一个字符 |
[!a] |
匹配任何一个字符除了中括号内声明的a字符 |
六、查询udev匹配键值
1、网卡
2、硬盘信息
3、硬盘大小
4、查看设备信息
如果是查看硬盘设置目录为/sys/block/下。
5、监控热插拔设备
6、查看硬盘属性
7、查看设备路径
七、udev规则赋值
1、运算符
= 给一个键赋值,它将覆盖之前的赋值
+= 给一个键追加值
:= 最后赋值给键;禁止其后任何变动,也就是防止之后的规则改动这个键值。
2、关键字
关键字 |
说明 |
NAME |
设置文件被创建的时候命名,或者网络接口设备将更该到的名字。Udev规定仅有一个规则可以设置设备名,所有其后的名称命名NAME关键字被忽略,如果想使用两个或两个以上的名字访问同一个设备的放可以考虑使用SYSLINK关键字 |
SYMLINK |
创建系统符号链接到设备文件。每条规则都可以为其需要控制的设备文件创建系统符号链接,如果在同一个SYSLINK关键字中定义多个系统符号链接,用空格将它们分开 |
OWNER,GROUP,MODE |
设置设备文件的系统权限,如规则中未定义这些关键字,则使用默认权限,如果定义了这些关键字,则覆盖原有转文权限 |
EVN{key} |
导出环境变量的值,这个关键字使用关依赖环境变量 |
RUN |
在规则读取的时候运行系统程序,可以用来创建设备文件,也可以是阻止某时间的发生,它能完成简单短小的命令操作。程序运行的时间长度和系统的操作复杂度有关。简单说这个关键字定义了为设备而执行的程序列表 |
LABEL |
标记GOTO关键字跳转的在规则配置文件里为内部控制所采用的名字标签位置 |
GOTO |
跳转到LABEL关键字标记的位置,类似面向过程的变成语言中的GOTO关键字 |
IMPORT{type} |
导入一个文件或者执行一个程序后生成的规则集到当前文件 |
WAIT_FOR_SYSFS |
等待sysfs中特定的设备文件被查找到,主要用在时序和依赖问题 |
OPTIONS |
特定的选项:last_rule对这类设备终端规则执行;ignore_device忽略当前规则;igmore_remove忽略接下来的并移走请求。All_partitions为所有的磁盘分区创建设备文件 |
八、Udev规则转换
1、udev具有类似printf的字符转换功能
2、能简化和省略规则
3、支持NAME、SYSMLINK、PROGRAM、OWNER、GROUP和RUN关键。
长置换符 |
短转换符 |
说明 |
$kernel |
%k |
设置的内核名(例如:sda1) |
$number |
%n |
设备的内核名编号(例如:sdb3中 %n=3) |
$devpath |
%p |
设置的设备路径(例如:/block/sda/sda1代表其实位置在/sys/block/sda/sda1) |
$id |
%b |
设置在设备路径中出现的BUS,IDDRIVER,SYSFS中出现设备名 |
$sysfs{file} |
%s{file} |
当前或根设备的sysfs属性值 |
$env{key} |
%E{key} |
环境变量值 |
$major |
%M |
设备文件的从设备号 |
$result |
$c |
此值替换由PROGRAM关键定义执行的外部程序返回的字符串。如果要取字符串中的一部分,那么可以由空格分离字符串的位置为编号作为属性:%c{N}。如果要选择某编号之后的所有子字符串,可以使用+来扩展:%c{N+} |
$parent |
%P |
设备文件的根设备名 |
$root |
%r |
Udev_root设备的字符串 |
$tempnode |
%N |
在真实设备文件创建之前为一个外部程序提供临时的设备文件名。 |
%% |
%% |
使用%字符本身时应用 |
$$ |
$$ |
使用$字符本身时应用 |
九、实验
1、BUS==”usb”, ATTRS{serial}==”AA00000000001786″,ATTRS{product}==”Lovely Attache”,
NAME=”myusb%n”,RUN+=”/usr/bin/wall 1234″
2、BUS==”usb”, ATTRS{serial}==”AA00000000001786″,ATTRS{product}==”Lovely Attache”,
NAME=”myusb%n”,RUN=”/bin/sh /root/hello.sh”
3、自动加载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 -o rw,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”