Linuxのudev(Userspace DEVice manager)はカーネルがデバイスを検出した瞬間に動き出し、/dev/配下のデバイスファイルを作成・管理します。udev rulesを書けば「このシリアル番号のUSBストレージは必ず/dev/backup-diskとして現れる」「外付けディスクを挿したらバックアップスクリプトを自動実行する」という設計が実現できます。
この記事では、udev rulesの基本書式とudevadm infoによる属性確認、デバイス名固定の実践手順、接続時スクリプト実行の設計まで順を追って解説します。RHEL 9.4 / Ubuntu 24.04 LTSで動作確認済みです。
この記事のポイント
・udevはカーネルのueventを受けて/dev/を管理するデーモン
・rulesは/etc/udev/rules.d/に配置し番号順に評価される
・udevadm infoでATTR名を特定してから条件式を書く
・ACTION=="add", RUN+="スクリプト"でデバイス接続時に自動実行できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
udevとは何か(Linuxでデバイス名が変わる理由)
Linuxがデバイスを検出すると、カーネルは/sys/ファイルシステムにデバイス情報を展開してueventを送出します。このueventを受け取るのがudevdデーモンです。udevdはudev rulesを上から評価し、条件に一致した場合に/dev/配下のデバイスファイルを作成したり、シンボリックリンクを張ったりします。デバイス名がずれる根本原因は、カーネルがデバイスを検出する順序にあります。SATA/SASのディスクは接続ポートの順序で/dev/sda、/dev/sdb…と割り当てられますが、USB外付けディスクは接続のタイミングによって名前が変わります。udev rulesでSYMLINKを使えば、どのデバイス名が割り当てられていても「特定のシリアル番号のデバイスは常に/dev/backup-disk」として参照できます。
udevの設定ファイルは/etc/udev/配下に置かれます。
・/etc/udev/udev.conf:udevdの動作パラメータ(ログレベル等)
・/etc/udev/rules.d/*.rules:ユーザーが書くカスタムルール
・/lib/udev/rules.d/*.rules:ディストリビューションが提供するデフォルトルール
/etc/udev/rules.d/と/lib/udev/rules.d/のファイルは、ファイル名の数字部分の昇順で評価されます。数字が同じ場合はアルファベット順です。ユーザールールは99-*.rulesのように数字を大きくして最後に評価させるのが一般的です。
udevルールファイルの書き方
udev rulesの1行は「マッチキー==値」と「アクションキー=値」を組み合わせたカンマ区切りの式です。# 書式 MATCH_KEY=="値", MATCH_KEY=="値", ..., ACTION_KEY="値" # 例:ラベル名でUSBドライブを特定してシンボリックリンクを張る SUBSYSTEM=="block", ENV{ID_FS_LABEL}=="BACKUP_USB", SYMLINK+="backup-disk"
1. 代表的なマッチキー
・SUBSYSTEM:デバイスが属するサブシステム(block・net・usb・input など)・KERNEL:カーネルが割り当てたデバイス名(sdb・sdb1・ttyUSB0 など)のパターン
・ACTION:イベントの種類(add・remove・change)
・ATTR{属性名}:/sys/のデバイスエントリにある属性値(vendor・model など)
・ENV{変数名}:udevが設定する環境変数(ID_SERIAL・ID_FS_TYPE・ID_FS_LABEL など)
2. 代表的なアクションキー
・NAME:/dev/配下に作るデバイスファイル名(通常はSYMLINKを使う方が安全)・SYMLINK+=:デバイスファイルへのシンボリックリンクを追加する
・OWNER/GROUP:デバイスファイルの所有者・グループを指定する
・MODE:デバイスファイルのパーミッション(例:"0660")
・RUN+="スクリプトのパス":ルールにマッチした時に実行するコマンド
udevadm infoでデバイスの属性を確認する
rulesを書く前にまず属性を確認します。存在しないATTR名を書いてもルールは単に無視されるため、必ず実機で属性名を確認してから書いてください。1. udevadm info --query=all --name= で属性一覧を取得する
USBストレージを例に確認してみます。# /dev/sdb の属性一覧を取得する udevadm info --query=all --name=/dev/sdb
P: /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/host3/target3:0:0/3:0:0:0/block/sdb N: sdb L: 0 S: disk/by-id/usb-BUFFALO_Portable_HDD_XXXXXXXX-0:0 E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/host3/target3:0:0/3:0:0:0/block/sdb E: DEVNAME=/dev/sdb E: DEVTYPE=disk E: SUBSYSTEM=block E: ID_VENDOR=BUFFALO E: ID_MODEL=Portable_HDD E: ID_SERIAL=BUFFALO_Portable_HDD_XXXXXXXX E: ID_SERIAL_SHORT=XXXXXXXX E: ID_TYPE=disk E: ID_BUS=usb E: ID_USB_DRIVER=usb-storage E: ID_FS_TYPE=ext4 E: ID_FS_LABEL=BACKUP_USB E: MAJOR=8 E: MINOR=16
2. udevadm info -a -n で親デバイスの属性も調べる
USBのベンダーIDやプロダクトIDはブロックデバイス(/dev/sdb)の親デバイス(USBバスのエントリ)が持っています。親デバイスの属性まで調べたい場合は-a オプションを使います。# 親デバイスの属性まで再帰的に表示する udevadm info -a -n /dev/sdb | head -60
looking at device '/devices/.../block/sdb': KERNEL=="sdb" SUBSYSTEM=="block" looking at parent device '/devices/.../target3:0:0/3:0:0:0': KERNELS=="3:0:0:0" SUBSYSTEMS=="scsi" ATTRS{vendor}=="BUFFALO " ATTRS{model}=="Portable_HDD" ATTRS{serial}=="XXXXXXXX" looking at parent device '/devices/.../usb1/1-2': KERNELS=="1-2" SUBSYSTEMS=="usb" ATTRS{idVendor}=="0411" ATTRS{idProduct}=="01ce" ATTRS{serial}=="XXXXXXXX"
-aオプションの出力では、同一ルールの中で参照できる属性は同じ「looking at parent」ブロック内のものに限られます。異なる階層の属性を同一ルールで組み合わせることはできません。デバイス名を固定するルールを作成・適用する
1. シリアル番号でデバイスを特定するルールを書く
udevadm infoで確認したID_SERIALを使って、このデバイスを/dev/backup-diskとして参照できるシンボリックリンクを作成します。# /etc/udev/rules.d/99-backup-disk.rules を作成する # BUFFALO Portable HDD(シリアル: XXXXXXXX)を /dev/backup-disk として固定する SUBSYSTEM=="block", ENV{ID_SERIAL}=="BUFFALO_Portable_HDD_XXXXXXXX", SYMLINK+="backup-disk", GROUP="disk", MODE="0660"
2. ルールを反映して動作確認する
# ルールを再読み込みする udevadm control --reload-rules # 現在接続済みのデバイスに対してルールを再評価する udevadm trigger --subsystem-match=block # シンボリックリンクが作成されたか確認する ls -la /dev/backup-disk
lrwxrwxrwx 1 root root 3 Sep 10 12:34 /dev/backup-disk -> sdb
デバイス接続時にスクリプトを自動実行する
1. 接続イベントでスクリプトをRUN+=で起動する
デバイスが接続(add)された時にスクリプトを実行したい場合はACTIONとRUN+=を組み合わせます。# /etc/udev/rules.d/99-backup-disk.rules SUBSYSTEM=="block", ACTION=="add", ENV{ID_SERIAL}=="BUFFALO_Portable_HDD_XXXXXXXX", SYMLINK+="backup-disk", GROUP="disk", MODE="0660", RUN+="/usr/local/bin/usb-backup.sh"
2. 長時間処理はsystemd oneshotサービス経由にする
重要な注意点:udevdはRUN+=に指定したスクリプトを同期的に実行します。スクリプトが長時間かかるとudevdがブロックされ、その間に発生した他のデバイスイベントが遅延します。rsyncのように時間のかかる処理はudev rulesから直接呼ぶのではなく、systemdのoneshot型サービス経由で実行するのが正しい設計です。udev rulesからsystemctlを呼ぶ方法:
# udev rulesでsystemdサービスを起動する SUBSYSTEM=="block", ACTION=="add", ENV{ID_SERIAL}=="BUFFALO_Portable_HDD_XXXXXXXX", SYMLINK+="backup-disk", RUN+="/usr/bin/systemctl start usb-rsync-backup.service"
[Unit] Description=USB Backup via rsync [Service] Type=oneshot ExecStart=/usr/local/bin/usb-backup.sh StandardOutput=journal StandardError=journal
udevルールのデバッグとトラブルシュート
1. udevadm test でルールをシミュレーションする
rulesを書いたがSYMLINKが作られない、スクリプトが実行されないといったケースでは、まずudevadm testでシミュレーションを実行します。デバイスファイルには/dev/sdbではなく/sys/のデバイスパスを指定します。# /dev/sdb のデバイスパスを確認する udevadm info --query=path --name=/dev/sdb # 出力例: /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/host3/target3:0:0/3:0:0:0/block/sdb # シミュレーションを実行する(実際のデバイスファイルは変更しない) udevadm test /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/host3/target3:0:0/3:0:0:0/block/sdb
calling: test ... Reading rules file: /etc/udev/rules.d/99-backup-disk.rules ... LINK 'backup-disk' /etc/udev/rules.d/99-backup-disk.rules:2 ... creating symlink '/dev/backup-disk' to 'sdb' ... run: '/usr/local/bin/usb-backup.sh'
2. udevadm monitor でイベントをリアルタイム確認する
デバイスを抜き挿しした際のイベントをリアルタイムで確認できます。# 別ターミナルで実行しておく udevadm monitor --udev --subsystem-match=block
UDEV [12345.678901] add /devices/.../block/sdb (block) UDEV [12345.679012] add /devices/.../block/sdb/sdb1 (block)
3. よくあるエラーと対処
・SYMLINKが作られない:マッチ条件が正しくない。udevadm testで評価されているルール行を確認し、属性名・値のスペルを検証する・スクリプトが実行されない:スクリプトに実行権限がない(
chmod +x /usr/local/bin/usb-backup.shを実行)か、フルパスで指定していない・スクリプトは動くが/dev/backup-diskがない:ACTIONとSYMLINKを別々のルール行に書いた場合、SYMLINKを設定する行にはACTION条件を含めないか、別行に分けて書く
・変更が反映されない:
udevadm control --reload-rulesの実行を忘れているudevdのログはjournalctlで確認できます。
# udevdのログを直近50行確認する journalctl -u systemd-udevd -n 50
まとめ
udev rulesを活用することで、デバイス名の変動に依存しない堅牢なスクリプト設計が実現できます。| やりたいこと | コマンド・設定 |
|---|---|
| デバイスの属性を確認する | udevadm info --query=all --name=/dev/sdb |
| 親デバイスの属性まで確認する | udevadm info -a -n /dev/sdb |
| デバイス名(シンボリックリンク)を固定する | SYMLINK+="名前" を /etc/udev/rules.d/に記述 |
| ルールを反映する | udevadm control --reload-rules && udevadm trigger |
| ルールをシミュレーションする | udevadm test /sys/のデバイスパス |
| デバイスイベントをリアルタイム確認する | udevadm monitor --udev --subsystem-match=block |
| デバイス接続時にスクリプトを実行する | ACTION=="add", RUN+="スクリプトのフルパス" |
| udevdのログを確認する | journalctl -u systemd-udevd -n 50 |
デバイス管理からサービス設計まで、Linuxの実務スキルを体系的に学ぶ
udev rulesやsystemdのサービス設計は、Linuxサーバーを現場で安定運用するための知識の一部です。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の現場経験を持つ現役エンジニアが2日間ハンズオンで直接指導します。3,100名以上の受講者が現場で活用しているスキルを最短で習得できます。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:LinuxでTCP TIME_WAIT・CLOSE_WAITが大量発生した時の調査と対処手順|ssとsysctlで接続状態を把握する
- この記事の属するカテゴリ:Linuxtipsへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます