Linux下udev规则详解

如何运用udev?编写udev规则?通过udev更好的管理Linux设备?


udev是什么?

          udev 是 Linux2.6 内核里的一个功能,它替代了原来的 devfs,成为当前 Linux 默认的设备管理工具。udev 以守护进程的形式运行,通过侦听内核发出来的 uevent 来管理 /dev目录下的设备文件。不像之前的设备管理工具,udev 在用户空间 (user space) 运行,而不在内核空间 (kernel space) 运行。


devfs 概述:

        linux下有专门的文件系统用来对设备进行管理,devfs和sysfs就是其中两种。在2.6内核以前一直使用的是devfs,devfs挂载于/dev目录下,提供了一种类似于文件的方法来管理位于/dev目录下的所有设备,我们知道/dev目录下的每一个文件都对应的是一个设备,至于当前该设备存在与否先且不论,而且这些特殊文件是位于根文件系统上的,在制作文件系统的时候我们就已经建立了这些设备文件,因此通过操作这些特殊文件,可以实现与内核进行交互。但是devfs文件系统有一些缺点,例如:不确定的设备映射,有时一个设备映射的设备文件可能不同,例如我的U盘可能对应sda有可能对应sdb;没有足够的主/辅设备号,当设备过多的时候,显然这会成为一个问题;/dev目录下文件太多而且不能表示当前系统上的实际设备;命名不够灵活,不能任意指定等等。

sysfs 概述:

        正因为上述这些问题的存在,在linux2.6内核以后,引入了一个新的文件系统sysfs,它挂载于/sys目录下,跟devfs一样它也是一个虚拟文件系统,也是用来对系统的设备进行管理的,它把实际连接到系统上的设备和总线组织成一个分级的文件,用户空间的程序同样可以利用这些信息以实现和内核的交互,该文件系统是当前系统上实际设备树的一个直观反应,它是通过kobject子系统来建立这个信息的,当一个kobject被创建的时候,对应的文件和目录也就被创建了,位于/sys下的相关目录下,既然每个设备在sysfs中都有唯一对应的目录,那么也就可以被用户空间读写了。用户空间的工具udev就是利用了sysfs提供的信息来实现所有devfs的功能的,但不同的是udev运行在用户空间中,而devfs却运行在内核空间,而且udev不存在devfs那些先天的缺陷。很显然,sysfs将是未来发展的方向。

udev工作流程图:

 

udev规则文件:

       规则文件,在udev中至关重要,这些规则文件,默认是放在/etc/udev/rules.d/下,都是以 *.rules命名格式,例如10-dm.rules,60-persistent-storage.rules,执行规则文件,是根据开头的数字大小,同数字,根据字母顺序执行,且后面的规则文件会覆盖前面的规则;('NAME'动作除外,因为'NAME'只能操作一次,后续NAME操作无用,后面会描述。) 先举一个例子:

ACTION=="add", SUBSYSTEM=="net", DRIVERS=="?*", ATTR{type}=="1", PROGRAM="/lib/udev/rename_device", RESULT=="?*", NAME="$result"

      该条例子,就是重命名网卡,在centos 7中,添加了这条网卡重命名的规则,把eth0,这种网卡名,通过程序rename_device进行修改名称,返回结果作为新名称。

编写udev规则文件:

       要编写规则文件,要先了解规则文件的一些基础语法。上个例子中,也有一些描述,除了以“#”开头的行(注释),所有的非空行都被视为一条规则,但是一条规则不能扩展到多行。规则都是由多个 键值对(key-value pairs)组成,并由逗号隔开,键值对可以分为 条件匹配键值对( 以下简称“匹配键 ”) 和 赋值键值对( 以下简称“赋值键 ”),一条规则可以有多条匹配键和多条赋值键。匹配键是匹配一个设备属性的所有条件,当一个设备的属性匹配了该规则里所有的匹配键,就认为这条规则生效,然后按照赋值键的内容,执行该规则的赋值。


匹配键和赋值键操作符解释见下表:


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

udev规则匹配表:

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


udev一些特殊的值和替换值:
在键值对中的键和操作符都介绍完了,最后是值 (value).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:        临时设备名.
%%:           符号 % 本身.
$$:             符号 $ 本身.

编写udev规则

规则所需要的信息如何获取?现在举刚开始的时候的,连接硬盘的SN的规则文件,如下,'ID_BUS'如何知道是'ata',还是'scsi' ?为何 sata盘的serial是"ID_SERIAL_SHORT", 而sas盘的serial是"ID_SCSI_SERIAL"那?下面会介绍三步骤,来实现这些信息如何获取。

KERNEL=="sd*", ENV{DEVTYPE}=="disk", ENV{ID_BUS}=="ata", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-serial/$env{ID_SERIAL_SHORT}"
    KERNEL=="sd*", ENV{DEVTYPE}=="disk", ENV{ID_BUS}=="scsi", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-serial/$env{ID_SCSI_SERIAL}"

第一步: 获取KERNEL等一些基本信息:

[root/]#udevadm info -a -p /sys/block/sdb

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:01.0/0000:01:00.0/host0/port-0:0/expander-0:0/port-0:0:0/end_device-0:0:0/target0:0:0/0:0:0:0/block/sdb':
    KERNEL=="sdb"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{ro}=="0"
    ATTR{size}=="3907029168"
    ATTR{stat}=="    5871    14262   161060     5586     7604   947711  7642102  5248132        0    41792  5253712"
    ATTR{range}=="16"
    ATTR{discard_alignment}=="0"
    ATTR{events}==""
    ATTR{ext_range}=="256"
    ATTR{events_poll_msecs}=="-1"
    ATTR{alignment_offset}=="0"
    ATTR{inflight}=="       0        0"
    ATTR{removable}=="0"
    ATTR{capability}=="50"
    ATTR{events_async}==""

  looking at parent device '/devices/pci0000:00/0000:00:01.0/0000:01:00.0/host0/port-0:0/expander-0:0/port-0:0:0/end_device-0:0:0/target0:0:0/0:0:0:0':
    KERNELS=="0:0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS=="sd"
    ATTRS{rev}=="1D01"
    ATTRS{type}=="0"
    ATTRS{scsi_level}=="7"
    ATTRS{model}=="WDC WD2003FYYS-0"
    ATTRS{state}=="running"
    ATTRS{queue_type}=="none"
    ATTRS{sas_address}=="0x500605b030013781"
    ATTRS{iodone_cnt}=="0x4a58"
    ATTRS{iorequest_cnt}=="0x4a58"
    ATTRS{queue_ramp_up_period}=="120000"
    ATTRS{evt_capacity_change_reported}=="0"
    ATTRS{timeout}=="30"
    ATTRS{evt_media_change}=="0"
    ATTRS{ioerr_cnt}=="0x27e"
    ATTRS{queue_depth}=="32"
    ATTRS{vendor}=="ATA     "
    ATTRS{evt_soft_threshold_reached}=="0"
    ATTRS{device_blocked}=="0"
    ATTRS{evt_mode_parameter_change_reported}=="0"
    ATTRS{evt_lun_change_reported}=="0"
    ATTRS{evt_inquiry_change_reported}=="0"
    ATTRS{dh_state}=="detached"
    ATTRS{iocounterbits}=="32"
    ATTRS{eh_timeout}=="10"
    ATTRS{sas_device_handle}=="0x000b"

根据这些信息,可以写KERNEL==‘sdb',ATTRS{type}==0 等规则 ,SUBSYSTEMS==’scsi‘;
SUBSYSTEMS=="scsi"

第二步,ENV的一些参数,进行规则判断:

<pre name="code" class="plain">[root/]#<span style="font-family: Arial, Helvetica, sans-serif;">udevadm info -q env -p /sys/block/sdb</span>
DEVLINKS=/dev/disk/by-id/ata-WDC_WD2003FYYS-02W0B0_WD-WMAY00564609 /dev/disk/by-id/wwn-0x50014ee0ad290c17 /dev/disk/by-path/pci-0000:01:00.0-sas-0x500605b030013781-lun-0 /dev/disk/by-serial/WD-WMAY00564609DEVNAME=/dev/sdbDEVPATH=/devices/pci0000:00/0000:00:01.0/0000:01:00.0/host0/port-0:0/expander-0:0/port-0:0:0/end_device-0:0:0/target0:0:0/0:0:0:0/block/sdbDEVTYPE=diskID_ATA=1ID_ATA_DOWNLOAD_MICROCODE=1ID_ATA_FEATURE_SET_AAM=1ID_ATA_FEATURE_SET_AAM_CURRENT_VALUE=254ID_ATA_FEATURE_SET_AAM_ENABLED=1ID_ATA_FEATURE_SET_AAM_VENDOR_RECOMMENDED_VALUE=128ID_ATA_FEATURE_SET_APM=1ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=128ID_ATA_FEATURE_SET_APM_ENABLED=1ID_ATA_FEATURE_SET_HPA=1ID_ATA_FEATURE_SET_HPA_ENABLED=1ID_ATA_FEATURE_SET_PM=1ID_ATA_FEATURE_SET_PM_ENABLED=1ID_ATA_FEATURE_SET_PUIS=1ID_ATA_FEATURE_SET_PUIS_ENABLED=0ID_ATA_FEATURE_SET_SECURITY=1ID_ATA_FEATURE_SET_SECURITY_ENABLED=0ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=300ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=300ID_ATA_FEATURE_SET_SMART=1ID_ATA_FEATURE_SET_SMART_ENABLED=1ID_ATA_ROTATION_RATE_RPM=7200ID_ATA_SATA=1ID_ATA_SATA_SIGNAL_RATE_GEN1=1ID_ATA_SATA_SIGNAL_RATE_GEN2=1ID_ATA_WRITE_CACHE=1ID_ATA_WRITE_CACHE_ENABLED=1ID_BUS=ataID_MODEL=WDC_WD2003FYYS-02W0B0ID_MODEL_ENC=WDC\x20WD2003FYYS-02W0B0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20ID_PART_TABLE_TYPE=gptID_PATH=pci-0000:01:00.0-sas-0x500605b030013781-lun-0ID_PATH_TAG=pci-0000_01_00_0-sas-0x500605b030013781-lun-0ID_REVISION=01.01D01ID_SERIAL=WDC_WD2003FYYS-02W0B0_WD-WMAY00564609ID_SERIAL_SHORT=WD-WMAY00564609ID_TYPE=diskID_WWN=0x50014ee0ad290c17ID_WWN_WITH_EXTENSION=0x50014ee0ad290c17MAJOR=8MINOR=16SUBSYSTEM=blockTAGS=:systemd:USEC_INITIALIZED=573368

通过第二步,可以知道sdb的 ID_BUS 是ata,即是sata硬盘,而serial是在 ID_SERIAL_SHORT这个选项中;

第三步,一些SYSFS{xxx}的选项如何获取?

例如,我想把硬盘的block size为512/4096的硬盘进行区分,如何操作?

# cat /sys/block/sdb/queue/physical_block_size 
     512

可以知道,硬盘的block size是在上述路径中,则在规则文件中:只需$sysfs{queue/physical_block_size}既可以取的硬盘的block size 
即:sysfs的数据,都在sys/block/目录下可以获取。

通过上述三步,基本上,一般的规则文件,都可以进行写了。

注意事项

1、规则文件的结尾,要有回车符,不然会报错(查看是否有回车符,cat -A xxx 或者 od -cb xxx)


systemd-udevd[19730]: invalid key/value pair in file /etc/udev/rules.d/*** on line 27,starting at character 239 ('')

2、启动顺序,

在zfs导入池中,一直发现,导入硬盘失败(而建raid的盘是用的syslink生成的SN码的盘);查看问题发现:

添加的规则文件,并没有放在最小内核里进行生成,导致了导入的时候,最小内核里没有规则文件,并没有生成连接,当然失败。

查看文件:

lsinitrd -f usr/lib/udev/rules.d/60-persistent-storage.rules
果然里面并没有添加的规则文件!
修改方法:
dracut --fstab

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咸鱼弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值