エルドラド カジノ

7.4. LFS システムにおけるデバイスとモジュールの扱い

第6章にて Udev パッケージをインストールしました。 このパッケージがどのように動作するかの詳細を説明する前に、デバイスを取り扱うかつての方法について順を追って説明していきます。

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

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

7.4.1. 開発経緯

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

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

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

7.4.2. Udev の実装

7.4.2.1. Sysfs ファイルシステム

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

7.4.2.2. Udev ブートスクリプト

初期起動スクリプト /etc/rc.d/init.d/udev は、Linux のブート時にデバイスノードの生成を受け持ちます。 このスクリプトは /sbin/hotplug のデフォルトから uevent ハンドラを取り除きます。 この時点でカーネルは、他の実行モジュールを呼び出す必要がないからです。 そのかわりに、カーネルが起動する uevent をネットリンクソケット (netlink socket) 上で待ち受けます。 そしてブートスクリプトが /lib/udev/devices 内にある静的なデバイスノードをすべて /dev にコピーします。 デバイスやディレクトリ、シンボリックリンクがこの時点で利用可能になっていないと、システム起動の初期段階において動的デバイスを扱う処理が動作しないためです。 あるいは udevd 自身がそれを必要とするからでもあります。 /lib/udev/devices 内に静的なデバイスノードを生成することで、動的デバイスを取り扱うことができないデバイスも動作させることができます。 こうしてブートスクリプトは Udev デーモン、つまり udevd を起動し、それがどのような uevent であっても対応できるものとなります。 最後にブートスクリプトはカーネルに対して、すべてのデバイスにおいて既に登録されている uevent を再起動させ、udevd がそれを待ち受けるものとなります。

初期起動スクリプト /etc/rc.d/init.d/udev_retry は、サブシステムに対するイベントの再起動を行ないます。 そのサブシステムとはファイルシステムに依存するもので、mountfs が実行されるまでマウントされません。 (特に /usr や /var がこれに該当します。) mountfs スクリプトの後にこのスクリプトが実行されるので、(イベントが再起動されるものであれば) 二度目には成功します。 このスクリプトは /etc/sysconfig/udev_retry ファイルにより設定が可能で、コメントを除く記述項目はすべてサブシステム名を表わし、二度目の起動時のリトライ対象となります。 (デバイスのサブシステムを知るには udevadm info --attribute-walk を実行します。)

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

udev の最近のバージョンより udevd はデバイスファイルを /dev には作らなくなりました。 このかわりに devtmpfs ファイルシステムを通じて、カーネルが制御していくものになりました。 デバイスノードを登録しようとするドライバーは (デバイスコア経由で) devtmpfs を通じて登録を行います。 devtmpfs のインスタンスが /dev 上にマウントされると、デバイスノードには固定的な名称、パーミッション、所有者の情報が設定され生成されます。

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

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

7.4.2.4. モジュールのロード

モジュールとしてコンパイルされたデバイスドライバーの場合、デバイス名の別名が作り出されています。 その別名は 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 サポートといった各種モジュールも、要求に応じてロードすることもできます。

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

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

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

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

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

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

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

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

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

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

7.4.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 ファイルにて、そのモジュール名を単独の行に記述することで実現します。 この方法はラッパーモジュールに対しても動作しますが、この場合は次善策となります。

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

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

blacklist forte

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

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

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

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

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

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

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

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

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

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

7.4.4. 参考情報

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