1 udevとは?
udevはデバイスファイルを動的に作成、削除する仕組みです。以前は、あらかじめコンピュータに接続する可能性のあるすべてのデバイスのデバイスファイルを作成していましたが、使用しないデバイスファイルを作成するのは無駄なので、udevのように動的にデバイスファイルを作成する仕組みが考案されました。
以下にデバイスファイルを作成するまでの流れを示します。
(1) カーネルがデバイスの追加、削除を検知
(2) /sys配下にデバイスの情報を反映
(3) systemd-udevdにイベント通知(Netlinkでイベント通知)
(4) systemd-udevdは、/sys配下の情報とudevルールを参照
(5) (4)の結果にもとづいて、デバイスファイルを作成
+-----------------------------+ | udev rule | | /usr/lib/udev/rules.d | | /etc/udev/rules.d | +-----------------------------+ A | | (4)参照 | (5)デバイスファイル作成 +-----------------------------+ +---------------+ | systemd-udevd |---------------------->| | +-----------------------------+ +---------------+ A | | | (4)参照 | | +---------------+ (3)uevent +-------------------------->| /sys | | +---------------+ | A | | (2)デバイス情報作成 | | +---------------------------------------------------------------------+ | Kernel | +---------------------------------------------------------------------+ A | | (1) カーネルがデバイスの追加、削除を検知
2 検証環境
CentOS版数は以下のとおりです。
[root@server ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core)
カーネル版数は以下のとおりです。
[root@server ~]# uname -r 3.10.0-1160.el7.x86_64
3 ルールの概要
3.1 ルールファイルの保存場所
udevのルールファイルは、以下の場所に保存されていいます。本記事では、/etc/udev/rules.d配下にルールを作成します。
パス | 概要 |
---|---|
/usr/lib/udev/rules.d | デフォルトのルールが保存されている場所 |
/etc/udev/rules.d | デフォルトルールをカスタマイズしたルールを置く場所 |
3.2 ルールファイルの規則
・ルールファイルの拡張子は、.rulesになります。
・ルールファイルの名前は、数字-名前から構成されます。 (例) 50-udev-default.rules
・ルールファイルのルールは、カンマで区切られたkey-valueペアで構成されています。
"=="で検索条件、"="でアクションを指定します。全ての検索条件に一致したら、アクションを実行します。
検索条件 アクション |<------------------------->| |<---------------->| XX=="xx", YY=="yy", ZZ=="zz", AA="aa", BB="bb"
(注) "アクション"という言葉は正式な用語ではないかもしれませんが、ここでは便宜上使用します。
4 ループバックデバイスを使った実験
ここでは、ループバックデバイスを作成して、udevルールの動作確認をしてみます。
ループバックデバイスを作成するためファイルを作成します。ファイルの作り方は、ファイルの作り方 - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# fallocate -l 1G disk1.img
作成したファイルを確認します。
[root@server ~]# ls -l disk1.img -rw-r--r--. 1 root root 1073741824 4月 27 20:44 disk1.img
loop0を検出したらloggerコマンドを実行するudevルールを作成します。
[root@server ~]# vi /etc/udev/rules.d/10-test.rules [root@server ~]# cat /etc/udev/rules.d/10-test.rules KERNEL=="loop0", RUN="/usr/bin/logger loop0"
カーネルからsystemd-udevdへのueventを監視するため、udevadmコマンドを実行します。
[root@server ~]# udevadm monitor -pk monitor will print the received events for: KERNEL - the kernel uevent
ログを確認するため、journalctlコマンドを実行します。なお、journalctlコマンドの使い方は、journalctlコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# journalctl -f
ループバックデバイスを作成します。なお、losetupコマンドの使い方は、losetupコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# losetup -f /root/disk1.img
ループバックデバイスを作成すると、カーネルからsystemd-udevdに下記ueventが通知されます。
[root@server ~]# udevadm monitor -pk monitor will print the received events for: KERNEL - the kernel uevent KERNEL[3932.365933] change /devices/virtual/block/loop0 (block) ACTION=change DEVNAME=/dev/loop0 DEVPATH=/devices/virtual/block/loop0 DEVTYPE=disk MAJOR=7 MINOR=0 SEQNUM=2086 SUBSYSTEM=block KERNEL[3932.381176] change /devices/virtual/block/loop0 (block) ACTION=change DEVNAME=/dev/loop0 DEVPATH=/devices/virtual/block/loop0 DEVTYPE=disk MAJOR=7 MINOR=0 SEQNUM=2087 SUBSYSTEM=block
ログを確認すると、loggerコマンドの実行結果が出力されていることがわかります。
[root@server ~]# journalctl -f -snip- 4月 27 20:45:46 server root[8999]: loop0 4月 27 20:45:46 server root[9000]: loop0
ループバックデバイスを確認します。
[root@server ~]# losetup -l NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE /dev/loop0 0 0 0 0 /root/disk1.img
次の検証のため、ループバックデバイスを削除します。
[root@server ~]# losetup -d /dev/loop0
5 置換(substitution)の使い方
5.1 デバイスパスを表示する方法(%p)
デバイスパスは、/sys配下に作成されます。%pはデバイスパスに置換されます。ここで作成するルールは、loopデバイス(*)を検出したら、loggerコマンドを実行します。loggerコマンドで、loopデバイスのデバイスパスを表示します。
(*)loopデバイスのマイナ番号は0,1,2...に一致したものになりま
[root@server ~]# vi /etc/udev/rules.d/10-test.rules [root@server ~]# cat /etc/udev/rules.d/10-test.rules KERNEL=="loop*", RUN="/usr/bin/logger %p"
カーネルからsystemd-udevdへのueventを監視するため、udevadmコマンドを実行します。
[root@server ~]# udevadm monitor -pk monitor will print the received events for: KERNEL - the kernel uevent
ループバックデバイスを作成します
[root@server ~]# losetup -f /root/disk1.img
ループバックデバイスを作成すると、カーネルからsystemd-udevdに下記ueventが通知されます。
[root@server ~]# udevadm monitor -kp monitor will print the received events for: KERNEL - the kernel uevent KERNEL[6150.972289] change /devices/virtual/block/loop0 (block) ACTION=change DEVNAME=/dev/loop0 DEVPATH=/devices/virtual/block/loop0 DEVTYPE=disk MAJOR=7 MINOR=0 SEQNUM=2152 SUBSYSTEM=block KERNEL[6150.984853] change /devices/virtual/block/loop0 (block) ACTION=change DEVNAME=/dev/loop0 DEVPATH=/devices/virtual/block/loop0 DEVTYPE=disk MAJOR=7 MINOR=0 SEQNUM=2153 SUBSYSTEM=block
ログを確認すると、%pがデバイスパスに置換されたことがわかります。
[root@server ~]# journalctl -f 4月 27 20:57:25 server root[9029]: /devices/virtual/block/loop0 4月 27 20:57:25 server root[9030]: /devices/virtual/block/loop0
次の検証のため、ループバックデバイスを削除します。
[root@server ~]# losetup -d /dev/loop0
5.2 デバイス名を表示する方法(%k)
%kはデバイス名に置換するオプションです。
[root@server ~]# vi /etc/udev/rules.d/10-test.rules [root@server ~]# cat /etc/udev/rules.d/10-test.rules KERNEL=="loop*", RUN="/usr/bin/logger %k"
ループバックデバイスを作成します
[root@server ~]# losetup -f /root/disk1.img
[root@server ~]# journalctl -f 4月 27 21:00:08 server root[9044]: loop0 4月 27 21:00:08 server root[9045]: loop0
次の検証のため、ループバックデバイスを削除します。
[root@server ~]# losetup -d /dev/loop0
5.3 デバイスのプロパティを表示する方法
ここでは、デバイスのプロパティとして、メジャー番号、マイナー番号を表示してみます。
[root@server ~]# vi /etc/udev/rules.d/10-test.rules [root@server ~]# cat /etc/udev/rules.d/10-test.rules KERNEL=="loop*", RUN="/usr/bin/logger $env{MAJOR}:$env{MINOR}"
ループバックデバイスを作成します
[root@server ~]# losetup -f /root/disk1.img
ログを確認すると、$env{MAJOR}:$env{MINOR}が、7:0,7:1に置換されたことがわかります。
[root@server ~]# journalctl -f 4月 27 21:03:07 server root[9069]: 7:0 4月 27 21:03:07 server root[9070]: 7:0
6 検索条件の使い方
今までは、デバイスの検索条件としてKERNELを使ってきました。検索条件の絞り込みに使えるキーワードは、他にSUBSYSTEM,DRIVER,ENV等があります。詳細は、man udevを参照ください。ここでは、カーネルモジュールの作り方 - hana_shinのLinux技術ブログの「12 デバイスの登録、削除」のプログラムを使って、検索条件の絞り込みを確認してみます。
6.1 SUBSYSTEMを使った絞り込み
[root@server ~]# vi /etc/udev/rules.d/10-test.rules [root@server ~]# cat /etc/udev/rules.d/10-test.rules KERNEL=="test_device", SUBSYSTEM=="misc", RUN="/usr/bin/logger %k"
カーネルからsystemd-udevdへのueventを監視するため、udevadmコマンドを実行します。
[root@server ~]# udevadm monitor -kp monitor will print the received events for: KERNEL - the kernel uevent
ログを確認するため、journalctlコマンドを実行します。
[root@server ~]# journalctl -f
モジュールをロードします。
[root@server modules]# insmod test.ko
udevadmコマンドを実行すると、testモジュールの情報を確認することができます。
[root@server ~]# udevadm monitor -kp monitor will print the received events for: KERNEL - the kernel uevent KERNEL[4958.602017] add /module/test (module) ACTION=add DEVPATH=/module/test SEQNUM=2148 SUBSYSTEM=module KERNEL[4958.602323] add /devices/virtual/misc/test_device (misc) ACTION=add DEVNAME=/dev/test_device DEVPATH=/devices/virtual/misc/test_device MAJOR=10 MINOR=58 SEQNUM=2149 SUBSYSTEM=misc
ログを確認します。
[root@server ~]# journalctl -f 4月 27 21:11:04 server root[9667]: test_device
次の検証のため、モジュールをアンロードします。
[root@server modules]# rmmod test
次の検証のため、ループバックデバイスを削除します。
[root@server ~]# losetup -d /dev/loop0
6.2 ENVを使った絞り込み
[root@server ~]# vi /etc/udev/rules.d/10-test.rules [root@server ~]# cat /etc/udev/rules.d/10-test.rules KERNEL=="test_device", SUBSYSTEM=="misc", ENV{MAJOR}=="10", ENV{MINOR}=="55", RUN="/usr/bin/logger %k"
7 アクションの使い方
7.1 OWNER,GROUP,MODEの使い方
デバイスファイルにオーナ、グループ、パーミッションを設定することができます。ここでは、test_deviceを検出したら、デバイスファイルのパーミッションを0644に設定してみます。
[root@server ~]# vi /etc/udev/rules.d/10-test.rules [root@server ~]# cat /etc/udev/rules.d/10-test.rules KERNEL=="test_device", MODE="0644", RUN="/usr/bin/logger %k"
テスト用のモジュールをロードします。
[root@server modules]# insmod test.ko
[root@server ~]# journalctl -f 4月 27 21:28:18 server kernel: Device registered! 4月 27 21:28:18 server root[9717]: test_device
デバイスファイルのパーミッションを確認します。パーミッションが644であることがわかります。
[root@server ~]# ls -l /dev/test_device crw-r--r--. 1 root root 10, 57 4月 27 21:28 /dev/test_device
7.2 NAMEの使い方
br0ならtest-br0,br1ならtest-br1という名前のデバイスファイルを作成するルールを作成します。
[root@server ~]# vi /etc/udev/rules.d/10-test.rules [root@server ~]# cat /etc/udev/rules.d/10-test.rules KERNEL=="br*", NAME="test-br%n"
br0という名前のブリッジデバイスを作成します。
[root@server ~]# ip link add br0 type bridge
作成したブリッジを確認します。デバイス名がbr0ではなく、test-br0になっていることがわかります。
[root@server ~]# ip l -snip- 5: test-br0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 4e:3f:9e:fe:a5:c6 brd ff:ff:ff:ff:ff:ff
ブリッジデバイス作成時のカーネルからsystemd-udevdへのイベントは以下のようになります。
[root@server ~]# udevadm monitor -kp monitor will print the received events for: KERNEL - the kernel uevent KERNEL[4583.714316] add /devices/virtual/net/br0 (net) ACTION=add DEVPATH=/devices/virtual/net/br0 DEVTYPE=bridge IFINDEX=9 INTERFACE=br0 SEQNUM=2130 SUBSYSTEM=net KERNEL[4583.714373] add /devices/virtual/net/br0/queues/rx-0 (queues) ACTION=add DEVPATH=/devices/virtual/net/br0/queues/rx-0 SEQNUM=2131 SUBSYSTEM=queues KERNEL[4583.714398] add /devices/virtual/net/br0/queues/tx-0 (queues) ACTION=add DEVPATH=/devices/virtual/net/br0/queues/tx-0 SEQNUM=2132 SUBSYSTEM=queues KERNEL[4583.719449] move /devices/virtual/net/test-br0 (net) ACTION=move DEVPATH=/devices/virtual/net/test-br0 DEVPATH_OLD=/devices/virtual/net/br0 DEVTYPE=bridge IFINDEX=9 INTERFACE=test-br0 SEQNUM=2133 SUBSYSTEM=net