hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

udevルールの書き方



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