Linux再起動によるデバイス名の変動をudevで固定化する(PiKVMのCSIカメラにおける実践例)

Raspberry Pi 3 Model Bをベースに構築したPiKVM環境において、サーバ前面のLEDインジケータや物理的な稼働状態を外部から確認するため、CSI接続のカメラモジュールを導入しました。映像配信デーモンには軽量かつ低遅延な ustreamer(サービス名:ustreamer-csi)を採用し、ポート8081経由でMJPEGストリームを配信する構成をとっています。

初期構築時は設定ファイル内で /dev/video0 を直接指定しており、Webブラウザ経由で問題なく映像をプレビューできる状態を確認していました。

目次

広告

再起動後も安定してカメラデバイスを認識させる仕組みの導入

しかし、システムを再起動した後にuStreamerの配信状態を確認したところ、Webサーバ自体は応答するものの映像ストリームが停止している現象が発生しました。

APIエンドポイント(/state)を確認したところ、以下のように “online": false かつ “captured_fps": 0 となっており、デバイスからフレームが1枚も取得できていませんでした。

{
"ok": true,
"result": {
"source": {
"resolution": {
"width": 640,
"height": 480
},
"online": false,
"desired_fps": 0,
"captured_fps": 0
},
"stream": {
"queued_fps": 0,
"clients": 0
}
}
}

再起動のたびにSSHでログインしてデバイス番号を確認し、サービスを手動で再設定する運用は現実的ではありません。OS再起動後も無人かつ確実にカメラデバイスを認識し、自動でストリーミングを復帰させる仕組みの導入が必要となりました。

/dev/video 番号の動的割り当てとRaspberry Pi特有のデバイス構造

障害の原因を突き止めるため、カーネルログを確認しました。

dmesg | grep video

出力結果の一部は以下の通りでした。

bcm2835-isp bcm2835-isp: Device node output[0] registered as /dev/video13
bcm2835-codec bcm2835-codec: Device registered as /dev/video10
bcm2835_v4l2-0: V4L2 device registered as video2 - stills mode > 1280x720
bcm2835-codec bcm2835-codec: Device registered as /dev/video18

カーネルの初期化順序により、CSIカメラ用の bcm2835-v4l2 デバイスが /dev/video2 として登録されていました。サービス定義側で /dev/video0 をハードコードしていたため、不一致が発生してオープンに失敗していたことが原因でした。

ここで課題となったのが、Raspberry Pi特有のV4L2デバイスの多さです。SoC内部のハードウェアコーデック(bcm2835-codec)や画像信号処理プロセッサ(bcm2835-isp)が多数のノード(video10〜video23 等)を生成するため、単に「空いているvideoノード」を指定するだけでは正確なカメラデバイスを特定できません。

また、古いドキュメントで見られる ATTR{name}=="mmal service 16.1″ という条件は現在の環境と一致せず、実機では camera0 と識別されている点にも注意が必要でした。

udevルールによる固定シンボリックリンク(/dev/csi-camera)の生成

デバイス番号自体の固定化を試みるのではなく、udevルールを用いて常にCSIカメラを指す一意のシンボリックリンク /dev/csi-camera を自動生成します。

実機属性の確認

まず、現在カメラとして認識されているデバイスからudev条件として利用可能な属性を抽出しました。

udevadm info --attribute-walk --name=/dev/video2

抽出された重要な属性は以下の通りです。

looking at device '/devices/platform/soc/3f00b840.mailbox/bcm2835-camera/video4linux/video2':
KERNEL=="video2"
SUBSYSTEM=="video4linux"
ATTR{index}"0"
ATTR{name}"camera0"

looking at parent device '/devices/platform/soc/3f00b840.mailbox/bcm2835-camera':
KERNELS=="bcm2835-camera"
SUBSYSTEMS=="vchiq-bus"
DRIVERS=="bcm2835-camera"

他のデバイスノードと誤認しないよう、ATTR{name}=="camera0″ に加え、親デバイスの KERNELS=="bcm2835-camera" および DRIVERS=="bcm2835-camera" を組み合わせることで一意性を確保します。

udevルールの作成と反映

PiKVMのファイルシステムを書き込み可能にし、ルールファイルを作成します。

rwnano /etc/udev/rules.d/99-csi-camera.rules

ファイル /etc/udev/rules.d/99-csi-camera.rules に以下の1行を記述します。

SUBSYSTEM=="video4linux", ATTR{name}"camera0", KERNELS"bcm2835-camera", DRIVERS=="bcm2835-camera", ATTR{index}=="0", SYMLINK+="csi-camera", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/csi-camera"

TAG+="systemd" と ENV{SYSTEMD_ALIAS}="/dev/csi-camera" を付与することで、udevがリンクを生成した際にsystemdのデバイスユニット(dev-csi\x2dcamera.device)として認識されるようになります。 設定後、ルールを再読み込みして反映します。

udevadm control --reload-rules
udevadm trigger --subsystem-match=video4linux
udevadm settle
ro

シンボリックリンクの確認

リンクが正常に生成されているか確認します。

ls -l /dev/csi-camera
readlink -f /dev/csi-camera

/dev/csi-camera -> video2 のように実デバイスを指していれば成功です。

固定デバイスを利用したsystemdサービスの安定起動設定

生成した /dev/csi-camera をuStreamerサービスから利用します。単にパスを変更するだけでなく、systemdの起動順序制御(デバイス待ち受け)を設定することで、カメラデバイスの認識前にサービスが起動して失敗する競合を防ぎます。

systemdデバイスユニット名の確認

systemdが扱うエスケープ処理済みのデバイスユニット名を確認します。

systemd-escape --path --suffix=device /dev/csi-camera

出力結果:

dev-csi\x2dcamera.device

サービス定義の更新サービス設定ファイルを編集し、固定パスの指定と依存関係を追加します。

rwnano /etc/systemd/system/ustreamer-csi.service

設定ファイル /etc/systemd/system/ustreamer-csi.service の内容は以下の通りです。

[Unit]
Description=uStreamer for CSI Camera
Requires=dev-csi\x2dcamera.device
After=network.target dev-csi\x2dcamera.device

[Service]
ExecStart=/usr/bin/ustreamer --device=/dev/csi-camera --host=0.0.0.0 --port=8081
Restart=always
RestartSec=3
User=root

[Install]
WantedBy=multi-user.target

Requires および After に dev-csi\x2dcamera.device を指定したことで、udevによるデバイスリンク生成が完了したタイミングで確実にサービスが起動します。 設定を反映してサービスを再起動し、ファイルシステムを読み込み専用に戻します。

systemctl daemon-reload
systemctl restart ustreamer-csi
ro

/dev/csi-camera 経由での確実な配信開始と他デバイスへの応用性設定完了後、uStreamerの配信状態を再度確認しました。

curl -s http://127.0.0.1:8081/state

出力結果において “online": true となり、フレームレートが正常にカウントされ、ブラウザからもストリーム映像が途切れなく表示されることを確認しました。OSの再起動テストを複数回実施し、CSIカメラの物理番号が video0 や video2 に変動した場合でも、/dev/csi-camera を介してサービスが100%確実に起動することを確認できました。 この「udevで一意な条件から固定シンボリックリンクを生成し、TAG+="systemd" でサービスの起動順序を制御する」手法は、カメラに限らずUSBシリアル変換器(/dev/ttyUSB*)や外付けストレージなど、Linux上で番号変動が発生するあらゆるデバイスに応用が可能です。

まとめ

PiKVM再起動時にCSIカメラのデバイス番号が変動し、uStreamerの配信が停止する問題に対処しました。udevadm による属性調査から親デバイス情報を含む条件を特定し、udevルールで固定リンク /dev/csi-camera を生成。さらにsystemdのデバイス依存(Requires)と連携させることで、OS再起動後も無人かつ確実に映像配信が復帰する環境を確立しました。この手法はUSBシリアル等あらゆるLinuxデバイスに応用可能です。