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

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

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

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

7.9.1. 開発経緯

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

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

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

7.9.2. Udev の実装

7.9.2.1. Sysfs ファイルシステム

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

7.9.2.2. Udev ブートスクリプト

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

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

Udev はデバイスのメジャー番号、マイナー番号を認識するために /sys ディレクトリ内の sysfs の情報を参照します。 例えば /sys/class/tty/vcs/dev には 「7:0」 という文字があります。 この文字は udevd が利用するもので、メジャー番号が 7、マイナー番号が 0 のデバイスノードを生成します。 /dev ディレクトリ配下に生成されるノードの名称とパーミッションは、 /etc/udev/rules.d/ ディレクトリにある各種ファイルが指定する規則に従って決まります。 それらのファイルは番号付けがされています。 LFS-ブートスクリプトパッケージにおける方法に似ています。 Udev がデバイスを生成しようとしてその生成規則が見つけられなかった場合は、デフォルトのパーミッションは 660、デフォルトの所有者は root:root となります。 Udev におけるデバイス生成規則を設定するファイルについて、その文法を示したドキュメントが /usr/share/doc/udev-161/writing_udev_rules/index.html にあります。

7.9.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.9.2.5. ホットプラグ可能な/ダイナミックなデバイスの扱い

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

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

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

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

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

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

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

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

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

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

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

install snd-pcm /sbin/modprobe -i snd-pcm ; \
    /sbin/modprobe snd-pcm-oss ; true

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

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

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

blacklist forte

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

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

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

7.9.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.9.3.6. Udev がデバイスを生成しない問題

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

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

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

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

7.9.4. 参考情報

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