Keep learning, keep living...

0%

udev机制简介

udev机制是Linux kernel的设备管理机制. 当内核检测到设备插拔后, 会发送事件给用户态的udevd进程. 用户态udevd进程根据事件信息匹配不同规则从而进行不同的处理逻辑.

CentOS7中使用的是systemd中实现的udevd进程. udev规则文件的扩展名为.rules, 主要位于两个目录:

  • /etc/udev/rules.d/: 自定义规则
  • /usr/lib/udev/rules.d/: 系统自带规则

udev规则是以规则文件名按字母顺序进行匹配处理的, 一般文件名中会带有数字前缀, 如:50-udev-default.rules. 处理顺序与规则放在哪个目录下无关, 但如果不同目录下规则文件同名, /etc/udev/rules.d下的文件会覆盖/usr/lib/udev/rules.d/下的文件.

规则语法本身不是很复杂, 每条规则由一系列key/value对儿组成, 这些key/value对儿可以分为匹配赋值两种. 当规则中所有的匹配都满足时, 赋值部分的行为被调用. 每条规则至少要有一个匹配和一个赋值.

以下面规则举例:

1
KERNEL=="hdb", NAME="my_spare_disk"

匹配部分KERNEL=="hdb"表示设备的内核名称为hdb, 赋值部分NAME="my_spare_disk"表示用my_spare_disk作为设备节点的名称.

具体语法可以参考:

下面也一个实际例子来说明一下. 在我们的业务场景中需要自动监测虚拟机网络接口的创建和删除, 因为虚拟网卡也是内核中的设备文件, 因而可以通过udev规则来检测设备的创建和删除.

在计算节点上, 虚拟机的网络接口位于目录/sys/class/net:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CNA:~ # ls -l /sys/class/net/
total 0
lrwxrwxrwx 1 root root 0 Nov 30 02:38 arping-bond5 -> ../../devices/virtual/net/arping-bond5
lrwxrwxrwx 1 root root 0 Nov 30 02:38 bond5 -> ../../devices/virtual/net/bond5
-rw-r--r-- 1 root root 4096 Oct 31 20:29 bonding_masters
lrwxrwxrwx 1 root root 0 Nov 30 02:38 br1 -> ../../devices/virtual/net/br1
lrwxrwxrwx 1 root root 0 Nov 30 02:38 br-bond5 -> ../../devices/virtual/net/br-bond5
lrwxrwxrwx 1 root root 0 Nov 30 02:38 eth0 -> ../../devices/pci0000:ae/0000:ae:00.0/0000:af:00.0/net/eth0
lrwxrwxrwx 1 root root 0 Nov 30 02:38 eth1 -> ../../devices/pci0000:ae/0000:ae:00.0/0000:af:00.1/net/eth1
lrwxrwxrwx 1 root root 0 Nov 30 02:38 eth2 -> ../../devices/pci0000:ae/0000:ae:00.0/0000:af:00.2/net/eth2
lrwxrwxrwx 1 root root 0 Nov 30 02:38 eth3 -> ../../devices/pci0000:ae/0000:ae:00.0/0000:af:00.3/net/eth3
lrwxrwxrwx 1 root root 0 Nov 30 02:38 lo -> ../../devices/virtual/net/lo
lrwxrwxrwx 1 root root 0 Nov 30 02:38 Mgnt-0 -> ../../devices/virtual/net/Mgnt-0
lrwxrwxrwx 1 root root 0 Nov 30 02:38 ovs-system -> ../../devices/virtual/net/ovs-system
lrwxrwxrwx 1 root root 0 Nov 30 02:38 tap00000001.0 -> ../../devices/virtual/net/tap00000001.0
lrwxrwxrwx 1 root root 0 Nov 30 02:38 tap00000002.0 -> ../../devices/virtual/net/tap00000002.0

其中tap00000001.0tap00000002.0是虚拟机的网络接口.

可以通过udevadm info命令来查看可以匹配的条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
CNA:~ # udevadm info -ap /sys/class/net/tap00000001.0

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/virtual/net/tap00000001.0':
KERNEL=="tap00000001.0"
SUBSYSTEM=="net"
DRIVER==""
ATTR{dormant}=="0"
ATTR{ifalias}==""
ATTR{address}=="fe:6e:d4:89:ce:92"
ATTR{tun_flags}=="0x5102"
ATTR{broadcast}=="ff:ff:ff:ff:ff:ff"
ATTR{flags}=="0x1103"
ATTR{carrier}=="1"
ATTR{iflink}=="14"
ATTR{addr_assign_type}=="3"
ATTR{carrier_up_count}=="0"
ATTR{owner}=="-1"
ATTR{mtu}=="1500"
ATTR{addr_len}=="6"
ATTR{carrier_changes}=="0"
ATTR{ifindex}=="14"
ATTR{gro_flush_timeout}=="0"
ATTR{netdev_group}=="0"
ATTR{proto_down}=="0"
ATTR{group}=="-1"
ATTR{duplex}=="full"
ATTR{speed}=="10"
ATTR{link_mode}=="0"
ATTR{dev_id}=="0x0"
ATTR{operstate}=="unknown"
ATTR{dev_port}=="0"
ATTR{tx_queue_len}=="5000"
ATTR{type}=="1"
ATTR{carrier_down_count}=="0"

根据返回结果, 我们可以使用SUBSYSTEMKERNEL两个关键字来匹配设备事件, 使用RUN关键字来运行外部程序.

首先, 编写需要调用的脚本:vnic_hook.sh:

1
echo "ARGS: $@" >> /tmp/vnic_hook.log

为了简要说明, 我们只是简单地输出所有传入的参数到/tmp/vnic_hook.log中.

接着, 创建规则文件/etc/udev/rules.d/60-vnic.rules:

1
SUBSYSTEM=="net", KERNEL=="tap0000*", RUN+="/root/vnic_hook.sh $env{ACTION} %k"

规则中RUN关键字支持字符串替换, 其中$env{ACTION}表示事件行为, 如add, remove等, %k表示设备的内核名称, 细节参考:

udevd会自动检测规则变化, 因而我们不需要重启udevd.

可以使用udevadm test来检测规则是否正确:

1
udevadm test /sys/class/net/tap00000002.0

一切准备完成后, 我们关闭虚拟机, 此时虚拟网卡tap00000002.0被删除, 从/tmp/vnic_hook.log中可以看到:

1
ARGS: remove tap00000002.0

再次开启虚拟机, 从/tmp/vnic_hook.log中可以看到:

1
ARGS: add tap00000002.0

可以看到我们的脚本被正确执行了, 现在可以在脚本中实现具体业务逻辑了.

参考: