第 8 章の systemd のビルドを通じて udev デーモンをインストールしました。 この udev がどのように動作するかの詳細を説明する前に、デバイスを取り扱うかつての方法について順を追って説明していきます。
Linux システムは一般に、スタティックなデバイス生成方法を採用していました。 この方法では /dev
のもとに膨大な量の (場合によっては何千にもおよぶ) デバイスノードが生成されます。
実際にハードウェアデバイスが存在するかどうかに関わらずです。 これは MAKEDEV スクリプトを通じて生成されます。 このスクリプトからは
mknod
プログラムが呼び出されますが、その呼び出しは、この世に存在するありとあらゆるデバイスのメジャー/マイナー番号を用いて行われます。
udev による方法では、カーネルが検知したデバイスに対してのみ、デバイスノードが生成されます。
デバイスノードはシステムが起動するたびに生成されることになるので、 devtmpfs
ファイルシステム上に保存されます。 (devtmpfs
は仮想ファイルシステムであり、システムメモリ上に置かれます。)
デバイスノードの情報はさほど多くないので、消費するメモリ容量は無視できるほど少ないものです。
2000年2月に新しいファイルシステム devfs
がカーネル
2.3.46 に導入され、2.4系の安定版カーネルにて利用できるようになりました。
このファイルシステムはカーネルのソース内に含まれ実現されていましたが、デバイスを動的に生成するこの手法は、主要なカーネル開発者の十分な支援は得られませんでした。
devfs
が採用した手法で問題になるのは、主にデバイスの検出、生成、命名の方法です。 特にデバイスの命名方法がおそらく最も重大な問題です。
一般的に言えることとして、デバイス名が変更可能であるならデバイス命名の規則はシステム管理者が考えることであって、特定の開発者に委ねるべきことではありません。
また devfs
にはその設計に起因した競合の問題があるため、根本的にカーネルを修正しなければ解消できる問題ではありません。 そこで長い間
devfs
は非推奨 (deprecated) とされ、最終的に
2006年6月にはカーネルから取り除かれました。
開発版の 2.5 系カーネルと、後にリリースされた安定版のカーネル 2.6 系を経て、新しい仮想ファイルシステム
sysfs
が登場しました。 sysfs
が実現したのは、システムのハードウェア設定をユーザー空間のプロセスに対して提供したことです。
ユーザー空間での設定を可視化したことによって devfs
が為していたことを、ユーザー空間にて開発することが可能になったわけです。
sysfs
ファイルシステムについては上で簡単に触れました。
sysfs
はどのようにしてシステム上に存在するデバイスを知るのか、そしてどのデバイス番号を用いるべきなのか。 そこが知りたいところです。
カーネルに組み込まれて構築されたドライバーの場合は、対象のオブジェクトをカーネルが検出し、そのオブジェクトを
sysfs
(内部的には devtmpfs) に登録します。
モジュールとしてコンパイルされたドライバーの場合は、そのモジュールがロードされたときに登録されます。 sysfs
ファイルシステムが (/sys
に) マウントされると、ドライバーによって sysfs
に登録されたデータは、ユーザー空間のプロセスと (デバイスノードの修正を含む)
さまざまな処理を行う udevd にて利用可能となります。
デバイスファイルはカーネルによって、devtmpfs
ファイルシステム内に作り出されます。 デバイスノードを登録しようとするドライバーは (デバイスコア経由で)
devtmpfs
を通じて登録を行います。
devtmpfs
のインスタンスが /dev
上にマウントされると、デバイスノードには固定的な名称、パーミッション、所有者の情報とともに名前空間が公開されます。
この後にカーネルは udevd
に対して uevent を送信します。 udevd は、/etc/udev/rules.d
, /usr/lib/udev/rules.d
, /run/udev/rules.d
の各ディレクトリ内にあるファイルの設定ルールに従って、デバイスノードに対するシンボリックリンクを生成したり、パーミッション、所有者、グループの情報を変更したり、内部的な
udevd
データベースの項目を修正したりします。
上の三つのディレクトリ内にて指定されるルールは番号づけされており、三つのディレクトリの内容は一つにまとめられます。
デバイスノードの生成時に udevd
がそのルールを見つけ出せなかった時は、devtmpfs
が利用される際の初期のパーミッションと所有者の情報のままとなります。
モジュールとしてコンパイルされたデバイスドライバーの場合、デバイス名の別名が作り出されています。 その別名は
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 サポートといった各種モジュールも、要求に応じてロードすることもできます。
USB (Universal Serial Bus) で MP3 プレイヤーを接続しているような場合、カーネルは現在そのデバイスが接続されているということを認識しており、uevent が生成済の状態にあります。 その uevent は上で述べたように udevd が取り扱うことになります。
自動的にデバイスが生成される際には、いくつか問題が発生します。
udev がモジュールをロードできるためには、バス固有のエイリアスがあって、バスドライバーが sysfs
に対して適切なエイリアスを提供していることが必要です。
そうでない場合は、別の手段を通じてモジュールのロードを仕組まなければなりません。 Linux-6.7.4 においての udev
は、INPUT、IDE、PCI、USB、SCSI、SERIO、FireWire の各デバイスに対するドライバーをロードします。
それらのデバイスドライバーが適切に構築されているからです。
目的のデバイスドライバーが udev に対応しているかどうかは、modinfo
コマンドに引数としてモジュール名を与えて実行します。 /sys/bus
ディレクトリ配下にあるそのデバイス用のディレクトリを見つけ出して、modalias
ファイルが存在しているかどうかを見ることで分かります。
sysfs
に modalias
ファイルが存在しているなら、そのドライバーはデバイスをサポートし、デバイスとの直接のやり取りが可能であることを表します。
ただしエイリアスを持っていなければ、それはドライバーのバグです。 その場合は udev
に頼ることなくドライバーをロードするしかありません。 そしてそのバグが解消されるのを待つしかありません。
/sys/bus
ディレクトリ配下の対応するディレクトリ内に
modalias
ファイルがなかったら、これはカーネル開発者がそのバス形式に対する modalias のサポートをまだ行っていないことを意味します。
Linux-6.7.4 では ISA バスがこれに該当します。 最新のカーネルにて解消されることを願うしかありません。
Udev は snd-pcm-oss のような「ラッパー (wrapper)」ドライバーや loop のような、現実のハードウェアに対するものではないドライバーは、ロードすることができません。
「ラッパー
(wrapper)」モジュールが単に他のモジュールの機能を拡張するだけのものであるなら (例えば
snd-pcm-oss は snd-pcm の機能拡張を行うもので、OSS
アプリケーションに対してサウンドカードを利用可能なものにするだけのものであるため) modprobe
の設定によってラッパーモジュールを先にロードし、その後でラップされるモジュールがロードされるようにします。
これは以下のように、対応する /etc/modprobe.d/
ファイル内にて「softdep」の記述行を加えることで実現します。
<filename>
.conf
softdep snd-pcm post: snd-pcm-oss
「softdep」コマンドは pre:
を付与することもでき、あるいは pre:
と post:
の双方を付与することもできます。 その記述方法や機能に関する詳細は man ページ modprobe.d(5)
を参照してください。
問題のモジュールがラッパーモジュールではなく、単独で利用できるものであれば、 modules
ブートスクリプトを編集して、システム起動時にこのモジュールがロードされるようにします。 これは /etc/sysconfig/modules
ファイルにて、そのモジュール名を単独の行に記述することで実現します。
この方法はラッパーモジュールに対しても動作しますが、この場合は次善策となります。
不必要なモジュールはこれをビルドしないことにするか、あるいは /etc/modprobe.d/blacklist.conf
ファイルにブラックリスト
(blacklist) として登録してください。 例えば forte モジュールをブラックリストに登録するには以下のようにします。
blacklist forte
ブラックリストに登録されたモジュールは modprobe コマンドを使えば手動でロードすることもできます。
デバイス生成規則が意図したデバイスに合致していないと、この状況が往々にして起こります。 例えば生成規則の記述が不十分であった場合、SCSI ディスク (本来望んでいるデバイス) と、それに対応づいたものとしてベンダーが提供する SCSI ジェネリックデバイス (これは誤ったデバイス) の両方に生成規則が合致してしまいます。 記述されている生成規則を探し出して正確に記述してください。 その際には udevadm info コマンドを使って情報を確認してください。
この問題は、一つ前に示したものが別の症状となって現れたものかもしれません。 そのような理由でなく、生成規則が正しく
sysfs
の属性を利用しているのであれば、それはカーネルの処理タイミングに関わる問題であって、カーネルを修正すべきものです。
今の時点では、該当する sysfs
の属性の利用を待ち受けるような生成規則を生成し、/etc/udev/rules.d/10-wait_for_sysfs.rules
ファイルにそれを追加することで対処できます。 (/etc/udev/rules.d/10-wait_for_sysfs.rules
ファイルがなければ新規に生成します。) もしこれを実施してうまくいった場合は LFS 開発メーリングリストにお知らせください。
ここでは以下のことを前提としています。 まずドライバーがカーネル内に組み入れられて構築されているか、あるいは既にモジュールとしてロードされていること。 そして udev が間違った名前のデバイスを生成していないことです。
カーネルドライバーがそのデータを sysfs
にエクスポートしていない場合、udev はデバイスノード生成に必要な情報を得ていないことになります。
これはカーネルツリーの外に配置されるサードパーティ製のドライバーであれば当たり前のことです。 したがって /usr/lib/udev/devices
において、適切なメジャー、マイナー番号を用いた静的なデバイスノードを生成してください。 (カーネルのドキュメント
devices.txt
またはサードパーティベンダーが提供するドキュメントを参照してください。) この静的デバイスノードは、udev によって /dev
にコピーされます。
これは udev の設計仕様に従って発生するもので、uevent の扱いとモジュールのロードが平行して行われるためです。 このために命名順が予期できないものになります。 これを「固定的に」することはできません。 ですからカーネルがデバイス名を固定的に定めるようなことを求めるのではなく、シンボリックリンクを用いた独自の生成規則を作り出して、そのデバイスの固定的な属性を用いた固定的な名前を用いる方法を取ります。 固定的な属性とは例えば、udev によってインストールされるさまざまな *_id という名のユーティリティが出力するシリアル番号などです。 設定例については 「デバイスの管理」や 「全般的なネットワークの設定」を参照してください。
さらに参考になるドキュメントが以下のサイトにあります: