9.3. デバイスとモジュールの扱いについて

第 8 章systemd のビルドを通じて udev デーモンをインストールしました。 この udev がどのように動作するかの詳細を説明する前に、デバイスを取り扱うかつての方法について順を追って説明していきます。

Linux システムは一般に、スタティックなデバイス生成方法を採用していました。 この方法では /dev のもとに膨大な量の (場合によっては何千にもおよぶ) デバイスノードが生成されます。 実際にハードウェアデバイスが存在するかどうかに関わらずです。 これは MAKEDEV スクリプトを通じて生成されます。 このスクリプトからは mknod プログラムが呼び出されますが、その呼び出しは、この世に存在するありとあらゆるデバイスのメジャー/マイナー番号を用いて行われます。

udev による方法では、カーネルが検知したデバイスに対してのみ、デバイスノードが生成されます。 デバイスノードはシステムが起動するたびに生成されることになるので、 devtmpfs ファイルシステム上に保存されます。 (devtmpfs は仮想ファイルシステムであり、システムメモリ上に置かれます。) デバイスノードの情報はさほど多くないので、消費するメモリ容量は無視できるほど少ないものです。

9.3.1. 開発経緯

2000年2月に新しいファイルシステム devfs がカーネル 2.3.46 に導入され、2.4系の安定版カーネルにて利用できるようになりました。 このファイルシステムはカーネルのソース内に含まれ実現されていましたが、デバイスを動的に生成するこの手法は、主要なカーネル開発者の十分な支援は得られませんでした。

devfs が採用した手法で問題になるのは、主にデバイスの検出、生成、命名の方法です。 特にデバイスの命名方法がおそらく最も重大な問題です。 一般的に言えることとして、デバイス名が変更可能であるならデバイス命名の規則はシステム管理者が考えることであって、特定の開発者に委ねるべきことではありません。 また devfs にはその設計に起因した競合の問題があるため、根本的にカーネルを修正しなければ解消できる問題ではありません。 そこで長い間 devfs は非推奨 (deprecated) とされ、最終的に 2006年6月にはカーネルから取り除かれました。

開発版の 2.5 系カーネルと、後にリリースされた安定版のカーネル 2.6 系を経て、新しい仮想ファイルシステム sysfs が登場しました。 sysfs が実現したのは、システムのハードウェア設定をユーザー空間のプロセスに対して提供したことです。 ユーザー空間での設定を可視化したことによって devfs が為していたことを、ユーザー空間にて開発することが可能になったわけです。

9.3.2. Udev の実装

9.3.2.1. Sysfs ファイルシステム

sysfs ファイルシステムについては上で簡単に触れました。 sysfs はどのようにしてシステム上に存在するデバイスを知るのか、そしてどのデバイス番号を用いるべきなのか。 そこが知りたいところです。 カーネルに組み込まれて構築されたドライバーの場合は、対象のオブジェクトをカーネルが検出し、そのオブジェクトを sysfs (内部的には devtmpfs) に登録します。 モジュールとしてコンパイルされたドライバーの場合は、そのモジュールがロードされたときに登録されます。 sysfs ファイルシステムが (/sys に) マウントされると、ドライバーによって sysfs に登録されたデータは、ユーザー空間のプロセスと (デバイスノードの修正を含む) さまざまな処理を行う udevd にて利用可能となります。

9.3.2.2. デバイスノードの生成

デバイスファイルはカーネルによって、devtmpfs ファイルシステム内に作り出されます。 デバイスノードを登録しようとするドライバーは (デバイスコア経由で) devtmpfs を通じて登録を行います。 devtmpfs のインスタンスが /dev 上にマウントされると、デバイスノードには固定的な名称、パーミッション、所有者の情報とともに名前空間が公開されます。

この後にカーネルは udevd に対して uevent を送信します。 udevd は、/etc/udev/rules.d, /usr/lib/udev/rules.d, /run/udev/rules.d の各ディレクトリ内にあるファイルの設定ルールに従って、デバイスノードに対するシンボリックリンクを生成したり、パーミッション、所有者、グループの情報を変更したり、内部的な udevd データベースの項目を修正したりします。

上の三つのディレクトリ内にて指定されるルールは番号づけされており、三つのディレクトリの内容は一つにまとめられます。 デバイスノードの生成時に udevd がそのルールを見つけ出せなかった時は、devtmpfs が利用される際の初期のパーミッションと所有者の情報のままとなります。

9.3.2.3. モジュールのロード

モジュールとしてコンパイルされたデバイスドライバーの場合、デバイス名の別名が作り出されています。 その別名は modinfo プログラムを使えば確認することができます。 そしてこの別名は、モジュールがサポートするバス固有の識別子に関連づけられます。 例えば snd-fm801 ドライバーは、ベンダーID 0x1319 とデバイスID 0x0801 の PCI ドライバーをサポートします。 そしてpci:v00001319d00000801sv*sd*bc04sc01i*というエイリアスがあります。 たいていのデバイスでは、sysfs を通じてドライバーがデバイスを扱うものであり、ドライバーのエイリアスをバスドライバーが提供します。 /sys/bus/pci/devices/0000:00:0d.0/modalias ファイルならばpci:v00001319d00000801sv00001319sd00001319bc04sc01i00という文字列を含んでいるはずです。 udev が提供するデフォルトの生成規則によって udevd から /sbin/modprobe が呼び出されることになり、その際には uevent に関する環境変数 MODALIAS の設定内容が利用されます。 (この環境変数の内容は sysfs 内の modalias ファイルの内容と同じはずです。) そしてワイルドカードが指定されているならそれが展開された上で、エイリアス文字列に合致するモジュールがすべてロードされることになります。

上の例で forte ドライバーがあったとすると、snd-fm801 の他にそれもロードされてしまいます。 これは古いものでありロードされて欲しくないものです。 不要なドライバーのロードを防ぐ方法については後述しているので参照してください。

カーネルは、ネットワークプロトコル、ファイルシステム、NLS サポートといった各種モジュールも、要求に応じてロードすることもできます。

9.3.2.4. ホットプラグ可能な/ダイナミックなデバイスの扱い

USB (Universal Serial Bus) で MP3 プレイヤーを接続しているような場合、カーネルは現在そのデバイスが接続されているということを認識しており、uevent が生成済の状態にあります。 その uevent は上で述べたように udevd が取り扱うことになります。

9.3.3. モジュールロードとデバイス生成の問題

自動的にデバイスが生成される際には、いくつか問題が発生します。

9.3.3.1. カーネルモジュールが自動的にロードされない問題

udev がモジュールをロードできるためには、バス固有のエイリアスがあって、バスドライバーが sysfs に対して適切なエイリアスを提供していることが必要です。 そうでない場合は、別の手段を通じてモジュールのロードを仕組まなければなりません。 Linux-6.4.12 においての udev は、INPUT、IDE、PCI、USB、SCSI、SERIO、FireWire の各デバイスに対するドライバーをロードします。 それらのデバイスドライバーが適切に構築されているからです。

目的のデバイスドライバーが udev に対応しているかどうかは、modinfo コマンドに引数としてモジュール名を与えて実行します。 /sys/bus ディレクトリ配下にあるそのデバイス用のディレクトリを見つけ出して、modalias ファイルが存在しているかどうかを見ることで分かります。

sysfsmodalias ファイルが存在しているなら、そのドライバーはデバイスをサポートし、デバイスとの直接のやり取りが可能であることを表します。 ただしエイリアスを持っていなければ、それはドライバーのバグです。 その場合は udev に頼ることなくドライバーをロードするしかありません。 そしてそのバグが解消されるのを待つしかありません。

/sys/bus ディレクトリ配下の対応するディレクトリ内に modalias ファイルがなかったら、これはカーネル開発者がそのバス形式に対する modalias のサポートをまだ行っていないことを意味します。 Linux-6.4.12 では ISA バスがこれに該当します。 最新のカーネルにて解消されることを願うしかありません。

Udev は snd-pcm-oss のようなラッパー (wrapper)ドライバーや loop のような、現実のハードウェアに対するものではないドライバーは、ロードすることができません。

9.3.3.2. カーネルモジュールが自動的にロードされず Udev もロードしようとしない問題

ラッパー (wrapper)モジュールが単に他のモジュールの機能を拡張するだけのものであるなら (例えば snd-pcm-osssnd-pcm の機能拡張を行うもので、OSS アプリケーションに対してサウンドカードを利用可能なものにするだけのものであるため) modprobe の設定によってラッパーモジュールを先にロードし、その後でラップされるモジュールがロードされるようにします。 これは以下のように、対応する /etc/modprobe.d/<filename>.conf ファイル内にてsoftdepの記述行を加えることで実現します。

softdep snd-pcm post: snd-pcm-oss

softdepコマンドは pre: を付与することもでき、あるいは pre:post: の双方を付与することもできます。 その記述方法や機能に関する詳細は man ページ modprobe.d(5) を参照してください。

問題のモジュールがラッパーモジュールではなく、単独で利用できるものであれば、 modules ブートスクリプトを編集して、システム起動時にこのモジュールがロードされるようにします。 これは /etc/sysconfig/modules ファイルにて、そのモジュール名を単独の行に記述することで実現します。 この方法はラッパーモジュールに対しても動作しますが、この場合は次善策となります。

9.3.3.3. Udev が不必要なモジュールをロードする問題

不必要なモジュールはこれをビルドしないことにするか、あるいは /etc/modprobe.d/blacklist.conf ファイルにブラックリスト (blacklist) として登録してください。 例えば forte モジュールをブラックリストに登録するには以下のようにします。

blacklist forte

ブラックリストに登録されたモジュールは modprobe コマンドを使えば手動でロードすることもできます。

9.3.3.4. Udev が不正なデバイスを生成する、または誤ったシンボリックリンクを生成する問題

デバイス生成規則が意図したデバイスに合致していないと、この状況が往々にして起こります。 例えば生成規則の記述が不十分であった場合、SCSI ディスク (本来望んでいるデバイス) と、それに対応づいたものとしてベンダーが提供する SCSI ジェネリックデバイス (これは誤ったデバイス) の両方に生成規則が合致してしまいます。 記述されている生成規則を探し出して正確に記述してください。 その際には udevadm info コマンドを使って情報を確認してください。

9.3.3.5. Udev 規則が不審な動きをする問題

この問題は、一つ前に示したものが別の症状となって現れたものかもしれません。 そのような理由でなく、生成規則が正しく sysfs の属性を利用しているのであれば、それはカーネルの処理タイミングに関わる問題であって、カーネルを修正すべきものです。 今の時点では、該当する sysfs の属性の利用を待ち受けるような生成規則を生成し、/etc/udev/rules.d/10-wait_for_sysfs.rules ファイルにそれを追加することで対処できます。 (/etc/udev/rules.d/10-wait_for_sysfs.rules ファイルがなければ新規に生成します。) もしこれを実施してうまくいった場合は LFS 開発メーリングリストにお知らせください。

9.3.3.6. Udev がデバイスを生成しない問題

ここでは以下のことを前提としています。 まずドライバーがカーネル内に組み入れられて構築されているか、あるいは既にモジュールとしてロードされていること。 そして udev が間違った名前のデバイスを生成していないことです。

カーネルドライバーがそのデータを sysfs にエクスポートしていない場合、udev はデバイスノード生成に必要な情報を得ていないことになります。 これはカーネルツリーの外に配置されるサードパーティ製のドライバーであれば当たり前のことです。 したがって /usr/lib/udev/devices において、適切なメジャー、マイナー番号を用いた静的なデバイスノードを生成してください。 (カーネルのドキュメント devices.txt またはサードパーティベンダーが提供するドキュメントを参照してください。) この静的デバイスノードは、udev によって /dev にコピーされます。

9.3.3.7. 再起動後にデバイスの命名順がランダムに変わってしまう問題

これは udev の設計仕様に従って発生するもので、uevent の扱いとモジュールのロードが平行して行われるためです。 このために命名順が予期できないものになります。 これを固定的にすることはできません。 ですからカーネルがデバイス名を固定的に定めるようなことを求めるのではなく、シンボリックリンクを用いた独自の生成規則を作り出して、そのデバイスの固定的な属性を用いた固定的な名前を用いる方法を取ります。 固定的な属性とは例えば、udev によってインストールされるさまざまな *_id という名のユーティリティが出力するシリアル番号などです。 設定例については 「デバイスの管理」「全般的なネットワークの設定」を参照してください。

9.3.4. 参考情報

さらに参考になるドキュメントが以下のサイトにあります: