Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7087834
  • 博文数量: 703
  • 博客积分: 10821
  • 博客等级: 上将
  • 技术积分: 12042
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-02 10:41
个人简介

中科院云平台架构师,专注于数字化、智能化,技术方向:云、Linux内核、AI、MES/ERP/CRM/OA、物联网、传感器、大数据、ML、微服务。

文章分类

全部博文(703)

分类: 系统运维

2016-02-14 08:24:16

自2.6 核心开始,就可以使用udev 协助管理系统中各设备名称。例如,磁盘设备排序、网卡设备排序等。udev能动态地在/dev 目录里产生自定义的、标识性强的设备文件或设备链接。本文即以红旗Asianux 3.0 平台,给新加载的U盘设备自定义一个链接为例进行简要说明。

一、关于udev
2.4 内核使用devfs(设备文件系统)在设备初始化时创建设备文件,设备驱动程序可以指定设备号、所有者、用户空间等信息,devfs 运行在内核环境中,并有不少缺点:可能出现主/辅设备号不够,命名不灵活,不能指定设备名称等问题。而自2.6 内核开始,引入了sysfs 文件系统。sysfs 把连接在系统上的设备和总线组织成一个分级的文件,并提供给用户空间存取使用。udev 运行在用户模式,而非内核中。udev 的初始化脚本在系统启动时创建设备节点,并且当插入新设备——加入驱动模块——在sysfs上注册新的数据后,udev会创新新的设备节点。
udev 是一个工作在用户空间的工具,它能根据系统中硬件设备的状态动态的更新设备文件,包括设备文件的创建,删除,权限等。这些文件通常都定义在/dev 目录下,但也可以在配置文件中指定。udev 必须内核中的sysfs和tmpfs支持,sysfs 为udev 提供设备入口和uevent 通道,tmpfs 为udev 设备文件提供存放空间。
注意,udev 是通过对内核产生的设备文件修改,或增加别名的方式来达到自定义设备文件的目的。但是,udev 是用户模式程序,其不会更改内核行为。也就是说,内核仍然会创建sda,sdb等设备文件,而udev可根据设备的唯一信息来区分不同的设备,并产生新的设备文件(或链接)。而在用户的应用中,只要使用新产生的设备文件即可。
udev 的工作流程图:
 
简述udev的自定义规则 - zhuzhu - 津津计较
 
 

二、参考文档
正如前面提到的,udev 依赖于2.6 核心上的sysfs 文件系统。因此,只有在红旗DC 5.0以上版本中才能使用。随着udev 的不断发展,不同版本的udev 规则也有不少差别,编写规则时必须注意
因为不同版本的udev 规则定义方式不同,在编写时需特别留意。我主要是参考以下资料来了解相关规则的:
官网:http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
man udev
Writing udev rules
使用 udev 高效、动态地管理 Linux 设备文件

udev 服务的主要配置文件在/etc/udev/udev.conf,但通常不同修改。
规则文件是存放在/etc/udev/rules.d 目录下面,所有的规则文件都必须以.rules 作为后缀名。系统安装完毕后,该目录中就会有一些默认的规则文件,主要用于产生一些容易标识的设备符号链接。同时,一些应用程序,为了在/dev 下产生方便使用的标识符,也会放入一些规则,例如:40-multipath.rules、99-fuse.rules 等。
udev 是按照规则文件的字母顺序来解析各规则文件的,并根据匹配上的规则创建对应的设备文件或链接。所以,解析的顺序很重要,为了使自定义的规则生效,可以把规则写入较前的规则文件中,例如20-names.rules。

三、规则说明
以下规则说明来自使用 udev 高效、动态地管理 Linux 设备文件一文,与Asianux 3.0 中man udev 中的说明一致,我就不一一翻译了。
1、udev 规则的所有操作符
引用
“==”:比较键、值,若等于,则该条件满足;
“!=”: 比较键、值,若不等于,则该条件满足;
“=”: 对一个键赋值;
“+=”:为一个表示多个条目的键赋值。
“:=”:对一个键赋值,并拒绝之后所有对该键的改动。目的是防止后面的规则文件对该键赋值。

2、udev 规则的匹配键
引用
ACTION: 事件 (uevent) 的行为,例如:add( 添加设备 )、remove( 删除设备 )。
KERNEL: 内核设备名称,例如:sda, cdrom。
DEVPATH:设备的 devpath 路径。
SUBSYSTEM: 设备的子系统名称,例如:sda 的子系统为 block。
BUS: 设备在 devpath 里的总线名称,例如:usb。
DRIVER: 设备在 devpath 里的设备驱动名称,例如:ide-cdrom。
ID: 设备在 devpath 里的识别号。
SYSFS{filename}: 设备的 devpath 路径下,设备的属性文件“filename”里的内容。
例如:SYSFS{model}==“ST936701SS”表示:如果设备的型号为 ST936701SS,则该设备匹配该 匹配键。
在一条规则中,可以设定最多五条 SYSFS 的 匹配键。
ENV{key}: 环境变量。在一条规则中,可以设定最多五条环境变量的 匹配键。
PROGRAM:调用外部命令。
RESULT: 外部命令 PROGRAM 的返回结果。

3、udev 的重要赋值键
引用
NAME:在 /dev下产生的设备文件名。只有第一次对某个设备的 NAME 的赋值行为生效,之后匹配的规则再对该设备的 NAME 赋值行为将被忽略。如果没有任何规则对设备的 NAME 赋值,udev 将使用内核设备名称来产生设备文件。
SYMLINK:为 /dev/下的设备文件产生符号链接。由于 udev 只能为某个设备产生一个设备文件,所以为了不覆盖系统默认的 udev 规则所产生的文件,推荐使用符号链接。
OWNER, GROUP, MODE:为设备设定权限。
ENV{key}:导入一个环境变量。

4、udev 的值和可调用的替换操作符
引用
Linux 用户可以随意地定制 udev 规则文件的值。例如:my_root_disk, my_printer。同时也可以引用下面的替换操作符:
$kernel, %k:设备的内核设备名称,例如:sda、cdrom。
$number, %n:设备的内核号码,例如:sda3 的内核号码是 3。
$devpath, %p:设备的 devpath路径。
$id, %b:设备在 devpath里的 ID 号。
$sysfs{file}, %s{file}:设备的 sysfs里 file 的内容。其实就是设备的属性值。
$env{key}, %E{key}:一个环境变量的值。
$major, %M:设备的 major 号。
$minor %m:设备的 minor 号。
$result, %c:PROGRAM 返回的结果。
$parent, %P:父设备的设备文件名。
$root, %r:udev_root的值,默认是 /dev/。
$tempnode, %N:临时设备名。
%%:符号 % 本身。
$$:符号 $ 本身。

四、实例说明
下面以在Asianux 3.0 平台上为一块U盘创建一个新的设备文件为例说明。
1、查询设备属性
规则中,需要给出匹配的设备属性,例如设备的序列号、厂商ID、磁盘大小等,用于区分不同的设备。这有三种方法:
a、由udevinfo 命令得到
引用
# udevinfo -a -p /block/sda
  looking at device '/block/sda':
    KERNEL=="sda"
    SUBSYSTEM=="block"
    SYSFS{stat}=="      42       25      536     1882        0        0        0        0        0     1882     1882"
    SYSFS{size}=="128000"
......
  looking at parent device '/devices/pci0000:00/0000:00:03.0/usb1':
    ID=="usb1"
    BUS=="usb"
    DRIVER=="usb"
    SYSFS{configuration}==""
    SYSFS{serial}=="0000:00:03.0"
    SYSFS{product}=="OHCI Host Controller"
......

b、由sysfs 文件系统获得
引用
# cat /sys/block/sda/size
128000

c、通过外部命令
通过scsi_id、path_id、usb_id 等命令获取:
引用
# /lib/udev/usb_id -x /block/sda
ID_VENDOR=0c76
ID_MODEL=0005
ID_REVISION=0100
ID_SERIAL=0c76_0005
ID_TYPE=disk
ID_BUS=usb

※ 注意,可供选择的设备属性有不少,但必须选择固定不变的属性,例如厂商ID、大小等。换句话说,不能选择随重启或插入而改变的信息,例如pci_id等,否则,同样的规则下次将不能匹配成功。
2、创建规则

# cat 20-names.rules
SUBSYSTEM=="block",BUS=="usb",SYSFS{product}=="OHCI Host Controller",NAME="usbhd%n"

※ 注意,最后的%n,若没有这变量符,将不会创建该设备下分区的映射;另外,双等号“==”与等号“=”是不能混淆使用。

3、测试规则
引用
# udevtest /block/sda
main: looking at device '/block/sda' from subsystem 'block'
udev_rules_get_name: rule applied, 'sda' becomes 'usbhd'
run_program: '/lib/udev/usb_id -x'
run_program: '/lib/udev/usb_id' (stdout) 'ID_VENDOR=0c76'
run_program: '/lib/udev/usb_id' (stdout) 'ID_MODEL=0^'
run_program: '/lib/udev/usb_id' (stdout) 'ID_REVISION=0100'
run_program: '/lib/udev/usb_id' (stdout) 'ID_SERIAL=0c76_0^'
run_program: '/lib/udev/usb_id' (stdout) 'ID_TYPE=disk'
run_program: '/lib/udev/usb_id' (stdout) 'ID_BUS=usb'
run_program: '/lib/udev/usb_id' returned with status 0
udev_rules_get_name: 1 untrusted character(s) replaced
udev_rules_get_name: add symlink 'disk/by-id/usb-0c76_0_'
run_program: '/lib/udev/path_id /block/sda'
run_program: '/lib/udev/path_id' (stdout) 'ID_PATH=pci-0000:00:03.2-usb-0:1:1.0-scsi-0:0:0:0'
run_program: '/lib/udev/path_id' returned with status 0
udev_rules_get_name: add symlink 'disk/by-path/pci-0000:00:03.2-usb-0:1:1.0-scsi-0:0:0:0'
udev_device_event: device '/block/sda' already in database, validate currently present symlinks
udev_node_add: creating device node '/dev/usbhd', major = '8', minor = '0', mode = '0640', uid = '0', gid = '6'
udev_node_add: creating symlink '/dev/disk/by-id/usb-0c76_0_' to '../../usbhd'
udev_node_add: creating symlink '/dev/disk/by-path/pci-0000:00:03.2-usb-0:1:1.0-scsi-0:0:0:0' to '../../usbhd'
main: run: 'socket:/org/kernel/udev/monitor'
main: run: '/lib/udev/udev_run_devd'
main: run: 'socket:/org/freedesktop/hal/udev_event'
main: run: '/sbin/pam_console_apply /dev/usbhd /dev/disk/by-id/usb-0c76_0_ /dev/disk/by-path/pci-0000:00:03.2-usb-0:1:1.0-scsi-0:0:0:0'

从红色标记的地方可见,原来的sda设备被改为自定义的usbhd设备符(剩余信息是其他规则匹配的情况)。

4、启动udev
引用
# start_udev
启动 udev:                                                [确定]

5、测试
从/dev 目录下,可查看到新创建的设备符:
引用
# ll /dev/usbhd*
brw-r----- 1 root disk 8, 0 08-10 17:49 /dev/usbhd
brw-r----- 1 root disk 8, 1 08-10 17:50 /dev/usbhd1

挂载测试:
引用
# mount /dev/usbhd1 /mnt/disk
# mount|grep usbhd1
/dev/usbhd1 on /mnt/disk type vfat (rw)
# df |grep usbhd1
/dev/usbhd1              62952     52172     10780  83% /mnt/disk

可见,我们新创建的设备符已经生效,并可以使用。
※ 注意,由于我们使用的是NAME方式,即新的usbhd会直接替换为sda,因此,在fdisk -l 命令下,可能会看不到原来的sda设备。这时,可通过dmesg命令获取内核分配的设备符:
引用
# dmesg
usbcore: registered new driver usb-storage
USB Mass Storage support registered.
  Vendor:           Model:                   Rev:
  Type:   Direct-Access                      ANSI SCSI revision: 02
SCSI device sda: 128000 512-byte hdwr sectors (66 MB)
sda: Write Protect is off
sda: Mode Sense: 0b 00 00 08
sda: assuming drive cache: write through
SCSI device sda: 128000 512-byte hdwr sectors (66 MB)
sda: Write Protect is off
sda: Mode Sense: 0b 00 00 08
sda: assuming drive cache: write through
sda: sda1
sd 0:0:0:0: Attached scsi removable disk sda
usb-storage: device scan complete
sd 0:0:0:0: Attached scsi generic sg0 type 0

另外,为避免与内核产生的设备文件冲突,新创建的设备文件应使用自定义的易标识名称,而不要使用sdb/sdc 等,否则,可能会给其他应用(如ASM等)带来麻烦。

6、使用其他属性创建规则
上面,我们是直接使用udevinfo 得到的信息来编写规则,下面借助外部程序来匹配设备属性。
首先,编写一个自定义规则:
引用
# cat /lib/udev/select_usb_disk.sh
#!/bin/bash
/lib/udev/usb_id -x $1|grep ID_VENDOR|awk -F '=' {'print $2'}

给予可执行权限:

# chmod +x /lib/udev/select_usb_disk.sh

测试其输入的结果:

# /lib/udev/select_usb_disk.sh /block/sda
0c76

编写规则:
引用
# cat 20-names.rules
KERNEL=="sd*",PROGRAM="/lib/udev/select_usb_disk.sh %p",RESULT=="0c76",SYMLINK+="usbhd%n"

测试规则:
引用
# udevtest /block/sda
main: looking at device '/block/sda' from subsystem 'block'
run_program: '/lib/udev/select_usb_disk.sh /block/sda'
run_program: '/lib/udev/select_usb_disk.sh' (stdout) '0c76'

run_program: '/lib/udev/select_usb_disk.sh' returned with status 0
udev_rules_get_name: add symlink 'usbhd'
run_program: '/lib/udev/usb_id -x'
run_program: '/lib/udev/usb_id' (stdout) 'ID_VENDOR=0c76'
run_program: '/lib/udev/usb_id' (stdout) 'ID_MODEL=0^'
run_program: '/lib/udev/usb_id' (stdout) 'ID_REVISION=0100'
run_program: '/lib/udev/usb_id' (stdout) 'ID_SERIAL=0c76_0^'
run_program: '/lib/udev/usb_id' (stdout) 'ID_TYPE=disk'
run_program: '/lib/udev/usb_id' (stdout) 'ID_BUS=usb'
run_program: '/lib/udev/usb_id' returned with status 0
udev_rules_get_name: 1 untrusted character(s) replaced
udev_rules_get_name: add symlink 'disk/by-id/usb-0c76_0_'
run_program: '/lib/udev/path_id /block/sda'
run_program: '/lib/udev/path_id' (stdout) 'ID_PATH=pci-0000:00:03.2-usb-0:1:1.0-scsi-0:0:0:0'
run_program: '/lib/udev/path_id' returned with status 0
udev_rules_get_name: add symlink 'disk/by-path/pci-0000:00:03.2-usb-0:1:1.0-scsi-0:0:0:0'
udev_rules_get_name: no node name set, will use kernel name 'sda'
udev_device_event: device '/block/sda' already in database, validate currently present symlinks
udev_node_add: creating device node '/dev/sda', major = '8', minor = '0', mode = '0640', uid = '0', gid = '6'
udev_node_add: creating symlink '/dev/usbhd' to 'sda'
udev_node_add: creating symlink '/dev/disk/by-id/usb-0c76_0_' to '../../sda'
udev_node_add: creating symlink '/dev/disk/by-path/pci-0000:00:03.2-usb-0:1:1.0-scsi-0:0:0:0' to '../../sda'
main: run: 'socket:/org/kernel/udev/monitor'
main: run: '/lib/udev/udev_run_devd'
main: run: 'socket:/org/freedesktop/hal/udev_event'
main: run: '/sbin/pam_console_apply /dev/sda /dev/usbhd /dev/disk/by-id/usb-0c76_0_ /dev/disk/by-path/pci-0000:00:03.2-usb-0:1:1.0-scsi-0:0:0:0'

删除旧设备符:

# rm -f /dev/usbhd*

※ 注意,使用start_udev并不会删除原来由NAME在/dev 目录下创建的设备文件,并且可能与新规则产生冲突,导致start_udev服务启动超时或出错,所以,在测试规则时,应预先删除旧规则创建的设备符。拔 去对应的物理设备,udev 会自动删除规则创建的设备文件或链接,但期间若修改了规则,导致规则所对应的设备文件或链接不相符,则这些旧的设备文件或链接可能会被遗留下来。

启动udev,创建新设备符:
引用
# start_udev
# ll /dev/usbhd*
lrwxrwxrwx 1 root root 3 08-10 18:18 /dev/usbhd -> sda
lrwxrwxrwx 1 root root 4 08-10 18:18 /dev/usbhd1 -> sda1

挂载测试:
引用
# mount /dev/usbhd1 /mnt/disk
# mount |grep disk
/dev/sda1 on /mnt/disk type vfat (rw)

可见,新设备符可用。
※ 请特别留意,规则中使用的是SYMLINK,所以,创建的仅是一个指向内核产生的设备的链接,而非取代原来的设备符。所以,在挂载成功后,mount命令显示的也是实际的内核设备。这很容易引起误会,请特别小心。

至此,我要讲解的udev 自定义规则方式已完毕。除了用于存储设备外,其他如Network interface等也能用udev 来处理。例如最常见的就是Asianux 3.0 下网络设备名自动变更的问题,可通过创建自定义规则:
引用
SUBSYSTEM=="net", SYSFS{address}=="AA:BB:CC:DD:EE:FF", NAME="public_NIC"

绑定固定的MAC地址,即可轻松解决。这比修改/etc/sysconfig/network-scripts/ifcfg-ethx 要可靠和稳定得多。

但是,udev 也不是万能的。至少,在我的实践中,可能因为udev 版本太低等原因,DC 5.0 平台下(2.6.9-89 核心),udev 为039,配置RDAC后,udev 的规则无法匹配内核产生的sdb等设备,至今没找到解决办法。

不过,udev 毕竟提供了一条很好的处理设备名冲突、变更等问题引起故障的解决办法,应熟悉和充分利用。
阅读(3723) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~