Introduction
Unit Goal
Describe udev
Describe the /sys file system
Write custom udev rules
Unit Sections
What is udev?
The /sys File System
Monitoring udev
Writing Custom udev Rules
Hands-On Activitles
Explore the /sys File System
Monitoring udev
Write a Custom udev Rule
Unit Test
Write a Custom udev Rule for iSCSI Storage
What is udev
在早起的unix/linux设备中,存入/dev/下面的设备文件已经成为一组静态文件,无法动态控制其name等属性。如果在机器中有大量设备,我们则无法进行详细区分。
[root@node1 ~]# ls -rlt /etc/rc.d/rc.sysinit -rwxr-xr-x. 1 root root 19114 May 23 2012 /etc/rc.d/rc.sysinit
udev通过一个守护进程的方式(rc.sysinit)对设备节点进行管理。管理对象则为/dev目录下的设备文件。
使用udev有什么好处:
动态管理:udev的守护进程监听来自内核的uevent(device event),在进行添加/删除device的操作时,udev也通过接收到的uevent来判断删除/dev下的设备文件。从而避免出现在/dev/产生大量的空设备文件。
自定义命名规则:通过规则文件来强制的限定某些设备的设备文件名称,而不是通过插拔顺序成/dev/sda,/dev/sdb等等。
设定设备的所有者以及权限:同样可以通过规则文件来限定device 的所有者/组以及相关权限。
udev是如何实现的:
默认的udev范例:
[root@node1 dev]# ls -rlt |grep sr* brw-rw----. 1 root cdrom 11, 0 Oct 25 17:22 sr0 lrwxrwxrwx. 1 root root 3 Oct 25 17:22 cdrw -> sr0 lrwxrwxrwx. 1 root root 3 Oct 25 17:22 cdrom -> sr0 lrwxrwxrwx. 1 root root 3 Oct 25 17:22 dvdrw -> sr0 lrwxrwxrwx. 1 root root 3 Oct 25 17:22 dvd –> sr0
我们看到以上设备文件,发现cdrw,cdrom,dvdrw,dvd都是连接到sr0设备的。我们只有一个attached CD compatible optical drive(其实就是光驱)。当系统插入相关设备的时候(例如插入CD光盘),udevd就监听到了uevent,然后在配置文件找到相关的规则文件,如下:
[root@node1 rules.d]# more 70-persistent-cd.rules # This file was automatically generated by the /lib/udev/write_cd_rules # program, run by the cd-aliases-generator.rules rules file. # # You can modify it, as long as you keep each rule on a single # line, and set the $GENERATED variable.
# VMware_IDE_CDR10 (pci-0000:00:07.1-scsi-1:0:0:0) SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:07.1-scsi-1:0:0:0", SYMLINK ="cdrom", ENV{GENERATED}="1" SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:07.1-scsi-1:0:0:0", SYMLINK ="cdrw", ENV{GENERATED}="1" SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:07.1-scsi-1:0:0:0", SYMLINK ="dvd", ENV{GENERATED}="1" SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{ID_PATH}=="pci-0000:00:07.1-scsi-1:0:0:0", SYMLINK ="dvdrw", ENV{GENERATED}="1"
现在我们插入的CD光盘,那udev就匹配其中的一条rule,激活了/dev/cdrom。这样做的目的就是便于管理。通过以下可证实,我们插入dvd光盘,其实设备文件原本应该是/dev/sr0:
[root@node1 rules.d]# mount /dev/sr0 /media/dvd mount: block device /dev/sr0 is write-protected, mounting read-only [root@node1 rules.d]# cd /media/dvd/ [root@node1 dvd]# ls EFI Packages RELEASE-NOTES-gu-IN.html RELEASE-NOTES-or-IN.html RELEASE-NOTES-zh-TW.html EULA README RELEASE-NOTES-hi-IN.html RELEASE-NOTES-pa-IN.html repodata GPL RELEASE-NOTES-as-IN.html RELEASE-NOTES-it-IT.html RELEASE-NOTES-pt-BR.html ResilientStorage HighAvailability RELEASE-NOTES-bn-IN.html RELEASE-NOTES-ja-JP.html RELEASE-NOTES-ru-RU.html RPM-GPG-KEY-redhat-beta images RELEASE-NOTES-de-DE.html RELEASE-NOTES-kn-IN.html RELEASE-NOTES-si-LK.html RPM-GPG-KEY-redhat-release isolinux RELEASE-NOTES-en-US.html RELEASE-NOTES-ko-KR.html RELEASE-NOTES-ta-IN.html ScalableFileSystem LoadBalancer RELEASE-NOTES-es-ES.html RELEASE-NOTES-ml-IN.html RELEASE-NOTES-te-IN.html Server media.repo RELEASE-NOTES-fr-FR.html RELEASE-NOTES-mr-IN.html RELEASE-NOTES-zh-CN.html TRANS.TBL [root@node1 dvd]#
在进行分区的时候可能会提示你/dev/sr0 read only ,是因为你挂载了cdrom ,而分区系统并没有提示你/dev/cdrom read only ,原因可想而知。
Getting to Know Udev
1. udev and it’s associated daemon processes are started by: B
a. Runlevel3 and 5 only
b. By the rc.sysinit script
c. by chkconfig
d. a service sript in /etc/init.d
2. Which of the following statements is false about udev? D
a. udev creates device files or nodes.
b. udev can create symbolic links that point to device files.
c. udev users deamon processes called udevd
d. You can not change how udev creates or maintains device files.
3. True or False, udev can create device files with a specific user owner? A
a. True
b. False
The /sys File System
sysfs(/sys file system)是一个kernel维护文件系统,类似于 /proc。相同的地方是与/proc一样,/sys也不会持续变化。但是相比/proc,/sys导出内核数据的方式更为统一,并且组织的更好。/sys下面所包含的信息,由内核提供,主要为连接到系统的设备信息。例如/sys/block下面存放着块设备信息,/sys/block/sda下面放着第一块scsi硬盘的设置与信息。
/sys下面的信息,有些是可以修改的,有些是read only。例如/sys/block/sda/device/model 或者 /sys/block/sda/size 是只读不可修改的。而/sys/block/sda/queue是可以修改的。又比如/sys/block/sda/queue/scheduler 通过此文件就可以修改sda的IO调度器。
那么sysfs又是怎么跟udev交互工作的呢?在文章前部分的图解里面有说udevd通过uevent中的内容进行调度工作,这个uevent文件保存于/sys/block/sda下面。通过sys与udev的交互决定了/dev/下面的内容。
Block目录:包含所有的块设备 Devices目录:包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构 Bus目录:包含系统中所有的总线类型 Drivers目录:包括内核中所有已注册的设备驱动程序 Class目录:系统中的设备类型(如网卡设备,声卡设备等)
/sys/block和/sys/dev/block的区别:都是通过连接到设备文件,/sys/block下面存储的设备名,而/sys/dev/block里面存储的设备号。
Monitoring udev
当kernel识别到一个设备,恰将触发以下事件:
1. kernel会在/sys下面填充关于设备的架构。
2. kernel将发送uevent给udev
3. udev接收到uevent之后,查找/etc/udev/rules.d/下面的规则文件,查找到所匹配的规则,然后根据规则创建一个新的device node。
可以使用udevadm命令与udev进行互动。下图中,我们使用udevadm monitor监控udevent,然后连接到已经做好的iscsi上。
^C[root@node1 ~]# udevadm monitor monitor will print the received events for: UDEV - the event which udev sends out after rule processing KERNEL - the kernel uevent
KERNEL[1382973390.161443] add /devices/platform/host3 (scsi) KERNEL[1382973390.161793] add /devices/platform/host3/scsi_host/host3 (scsi_host) KERNEL[1382973390.161802] add /devices/platform/host3/iscsi_host/host3 (iscsi_host) KERNEL[1382973390.161813] add /devices/platform/host3/session1/iscsi_session/session1 (iscsi_session) KERNEL[1382973390.161822] add /devices/platform/host3/session1/connection1:0/iscsi_connection/connection1:0 (iscsi_connection) UDEV [1382973390.163582] add /devices/platform/host3 (scsi) UDEV [1382973390.167747] add /devices/platform/host3/iscsi_host/host3 (iscsi_host) UDEV [1382973390.169998] add /devices/platform/host3/scsi_host/host3 (scsi_host) UDEV [1382973390.171241] add /devices/platform/host3/session1/iscsi_session/session1 (iscsi_session) UDEV [1382973390.171317] add /devices/platform/host3/session1/connection1:0/iscsi_connection/connection1:0 (iscsi_connection) KERNEL[1382973390.414694] add /devices/platform/host3/session1/target3:0:0 (scsi) UDEV [1382973390.415049] add /devices/platform/host3/session1/target3:0:0 (scsi) KERNEL[1382973390.415213] add /devices/platform/host3/session1/target3:0:0/3:0:0:0 (scsi) KERNEL[1382973390.418277] add /devices/platform/host3/session1/target3:0:0/3:0:0:0/scsi_device/3:0:0:0 (scsi_device) KERNEL[1382973390.422508] add /devices/platform/host3/session1/target3:0:0/3:0:0:0/scsi_generic/sg2 (scsi_generic) KERNEL[1382973390.423154] add /devices/platform/host3/session1/target3:0:0/3:0:0:0/bsg/3:0:0:0 (bsg) KERNEL[1382973390.424946] add /devices/platform/host3/session1/target3:0:0/3:0:0:1 (scsi) KERNEL[1382973390.425341] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/scsi_disk/3:0:0:1 (scsi_disk) KERNEL[1382973390.425831] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/scsi_device/3:0:0:1 (scsi_device) KERNEL[1382973390.425951] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/scsi_generic/sg3 (scsi_generic) KERNEL[1382973390.426038] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/bsg/3:0:0:1 (bsg) KERNEL[1382973390.443417] add /devices/virtual/bdi/8:16 (bdi) UDEV [1382973390.444402] add /devices/virtual/bdi/8:16 (bdi) UDEV [1382973390.461691] add /devices/platform/host3/session1/target3:0:0/3:0:0:0 (scsi) UDEV [1382973390.469917] add /devices/platform/host3/session1/target3:0:0/3:0:0:0/scsi_device/3:0:0:0 (scsi_device) KERNEL[1382973390.472915] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/block/sdb (block) KERNEL[1382973390.472996] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/block/sdb/sdb1 (block) UDEV [1382973390.481391] add /devices/platform/host3/session1/target3:0:0/3:0:0:1 (scsi) UDEV [1382973390.482262] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/scsi_disk/3:0:0:1 (scsi_disk) UDEV [1382973390.485659] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/scsi_device/3:0:0:1 (scsi_device) UDEV [1382973390.511379] add /devices/platform/host3/session1/target3:0:0/3:0:0:0/bsg/3:0:0:0 (bsg) UDEV [1382973390.515498] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/bsg/3:0:0:1 (bsg) UDEV [1382973390.521762] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/scsi_generic/sg3 (scsi_generic) UDEV [1382973390.523266] add /devices/platform/host3/session1/target3:0:0/3:0:0:0/scsi_generic/sg2 (scsi_generic) UDEV [1382973390.649027] add /devices/platform/host3/session1/target3:0:0/3:0:0:1/block/sdb (block)
从KERNEL行以下打印出的就是当login iscsi设备时的uevent信息。注意,uevent中是以/sys作为根目录的,例如/devices目录其实/sys/devices目录。udev收到uevent之后,通过计算判断出iscsi device的架构,从而created block device files。从上图可以看出,新添加进的硬盘位 sdb。
udevadm的一个特性就是可以很方便的查找关于device的信息,就像刚才所使用的udevadm monitor,可以收集到当新设备被kernel识别后所发出的uevent,udev接收后创建块设备文件的一系列过程。udevadm info 可以在udev database中查看关于设备更多的信息。例如:
[root@node1 ~]# udevadm info --attribute-walk --name=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/platform/host4/session2/target4:0:0/4:0:0:1/block/sdb': KERNEL=="sdb" SUBSYSTEM=="block" DRIVER=="" ATTR{range}=="16" ATTR{ext_range}=="256" ATTR{removable}=="0" ATTR{ro}=="0" ATTR{size}=="2097152" ATTR{alignment_offset}=="0" ATTR{discard_alignment}=="0" ATTR{capability}=="52" ATTR{stat}==" 172 13 1480 62 0 0 0 0 0 62 62" ATTR{inflight}==" 0 0"
looking at parent device '/devices/platform/host4/session2/target4:0:0/4:0:0:1': KERNELS=="4:0:0:1" SUBSYSTEMS=="scsi" DRIVERS=="sd" ATTRS{device_blocked}=="0" ATTRS{type}=="0" ATTRS{scsi_level}=="6" ATTRS{vendor}=="IET " ATTRS{model}=="VIRTUAL-DISK " ATTRS{rev}=="0001" ATTRS{state}=="running" ATTRS{timeout}=="30" ATTRS{iocounterbits}=="32" ATTRS{iorequest_cnt}=="0xcb" ATTRS{iodone_cnt}=="0xcb" ATTRS{ioerr_cnt}=="0x1" ATTRS{modalias}=="scsi:t-0x00" ATTRS{evt_media_change}=="0" ATTRS{dh_state}=="detached" ATTRS{queue_depth}=="32" ATTRS{queue_ramp_up_period}=="120000" ATTRS{queue_type}=="none"
looking at parent device '/devices/platform/host4/session2/target4:0:0': KERNELS=="target4:0:0" SUBSYSTEMS=="scsi" DRIVERS==""
looking at parent device '/devices/platform/host4/session2': KERNELS=="session2" SUBSYSTEMS=="" DRIVERS==""
looking at parent device '/devices/platform/host4': KERNELS=="host4" SUBSYSTEMS=="scsi" DRIVERS==""
looking at parent device '/devices/platform': KERNELS=="platform" SUBSYSTEMS=="" DRIVERS==""
--attribute-walk Print all sysfs properties of the specified device that can be used in udev rules to match the specified device. It prints all devices along the chain, up to the root of sysfs that can be used in udev rules.
在以上的输出中我们可以看到设备的相关信息,如size,model,manufacturer,serial number,kernel module,power information或更多。注意查看looking at parent device,上图排出了多个目录,每个目录都是上层目录的父目录。这些目录都是在/sys下的。
使用udevadm info 需要指定一些参数 --name=sdb 表明我们要查询的设备时/dev/sdb。—attribute-walk 表示将输出sdb相关层次的属性信息。另外—attribute-walk中输出的属性可以用来我们写udev rules时使用。
Writing Custom udev Rules
编写udev rule在/etc/udev/rules.d/下面添加rule file,当kernel识别到device的时候,将在这里(和/lib/udev/rules.d)查找相关匹配的规则。规则文件命名最好按照命名规范,two-digits-function.rules 只有.rules后缀才能被内核解析。
inotify:它是一个内核用于通知用户空间程序文件系统变化的机制。
udevd使用inotify来监控udev rule文件的变化并直接应用,不需要我们手动进行加载。如果希望确保更改的udev rule生效,可以使用udevadm control –reload-rules 进行重新加载。
--reload-rules Signal udevd to reload the rules files. The udev daemon detects changes automatically, this option is usually not needed. Reloading rules does not apply any changes to already existing devices. (注:reloading rule不会对已存在的devices生效)
Writing a Custom Rule
定制udev规则分两部分,前一部分用于测试匹配,匹配内容包括设备类型,串口号,配置信息等;后一部分用于赋值,如符号链接,权限,配置信息等。前提是所有测试匹配都通过。
udev rule的基本语法为逗号隔开每一个条件,使用运算符号来进行匹配赋值,常用运算符号的内容如下:
== 匹配符,相等
!= 匹配符,不等
= 赋值,只分配单一值
= 赋值,值可以为一个list
:= 赋值并锁定,防止以后被修改。
Examples
SUBSYSTEM==”block”, KERNEL==”sd*” ,SYMLINK =”mydisks/%k”
在这个范例中,如果新加入设备的信息符合 SUBSYSTEM==”block”, KERNEL==”sd*” ,则在创建symbolic links时赋值SYMLINK =”mydisks/%k” 。在编写udev rule时可以用到通配符,这些通配符的使用方式如bash shell的一样(例如*,? and [])。
常用的变量通配符:
$kernel, %k The kernel name for this device.
$number, %n The kernel number for this device. For example, ′sda3′ has kernel number of ′3′
$devpath, %p The devpath of the device.
全部通配符的说明在udev的man手册可以找到。
这些变量的通配符取自device的attribute,例如范例中的%k ,在udevadm info –attribute-walk –name=sda 的结果中,可以找到kernel=”sda”,而这里赋值SYMLINK =“mydisks/%k” 会在/dev/下创建符号链接 /dev/mydisks/sda 这里的sda则为变量在属性中取到的kernel。
在以下的例子中将用到下面的变量
PROGRAM Execute a program. The key is true, if the program returns successfully. The device properties are made available to the executed program in the environment. The program′s output printed to stdout, is available in the RESULT key.
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.
这两个变量成对出现,PROGRAM中的命令将在可用的shell中执行,执行结果与RESULT的值进行匹配。
例如,我们先选取我们所用的PROGRAM:
[root@node1 rules.d]# scsi_id --whitelisted --replace-whitespace --device /dev/sdb 1IET_00010001
scsi_id是用来检索或者生成device唯一标示的工具。
所得到的结果就是我们需要匹配的PROGRAM。然后在编写策略。
SUBSYSTEM=="block", PROGRAM=="scsi_id --whitelisted --replace-whitespace --device $tempnode", RESULT=="1IET_00010001", SYMLINK ="customdisks/customlink%n"
写好之后,重新logon in sdb设备。然后在dev下面查看,就可以看到SYMLINK所设定的值了。
[root@node1 dev]# ls customdisks/customlink customdisks/customlink
SUBSYSTEM=="block", PROGRAM=="scsi_id --whitelisted --replace-whitespace --device $tempnode", RESULT=="1IET_00010001", RUN ="/usr/bin/wall New disk %k"
这个例子与上个例子相似,不过不是创建符号链接了,而是在匹配成功后,使用RUN变量执行一个wall的命令。 RUN跟PROGRAM都是执行命令,主要区别在于udev不会等待RUN的命令完成,所以RUN中只能写执行时间相对较短的命令。以下为RUN的解释:
RUN Add a program to the list of programs to be executed for a specific device. This can only be used for very short running tasks. Running an event process for a long period of time may block all further events for this or a dependent device. Long running tasks need to be immediately detached from the event process itself. If the option RUN{fail_event_on_error} is specified, and the executed program returns non-zero, the event will be marked as failed for a possible later handling.
[root@node1 rules.d]# iscsiadm -m node -T iqn.2008-09.com.example:server.target10 -l Logging in to [iface: default, target: iqn.2008-09.com.example:server.target10, portal: 192.168.1.4,3260] (multiple) Login to [iface: default, target: iqn.2008-09.com.example:server.target10, portal: 192.168.1.4,3260] successful. [root@node1 rules.d]# Broadcast message from root@node1.example.com (Thu Oct 31 01:02:07 2013):
New disk sdb
打开两个会话,在重新Login target的时候,每个会话都会执行wall命令,弹出New disk sdb。
SUBSYSTEM=="block", KERNEL=="sdb*", OWNER="student", GROUP="disk", MODE="0640"
通过以上范例,在创建分区的时候,例如sdb1,sdb2,属主,属组和权限都会响应说设置,同样,我们重新login target
[root@node1 dev]# ls -rlt sdb* brw-r-----. 1 root disk 8, 16 Oct 31 01:14 sdb brw-r-----. 1 root disk 8, 17 Oct 31 01:14 sdb1 brw-r-----. 1 root disk 8, 19 Oct 31 01:14 sdb3