第6章にて Udev パッケージをインストールしました。 このパッケージがどのように動作するかの詳細を説明する前に、デバイスを取り扱うかつての方法について順を追って説明していきます。
Linux システムは一般に、スタティックなデバイス生成方法を採用していました。 この方法では /dev
のもとに膨大な量の (場合によっては何千にもおよぶ) デバイスノードが生成されます。
現実に存在するハードウェアデバイスが存在するかどうかに関わらずです。 これは MAKEDEV スクリプトを通じて生成されます。 このスクリプトからは
mknod
プログラムが呼び出されますが、その呼び出しは、この世に存在するありとあらゆるデバイスのメジャー/マイナー番号を用いて行われます。
Udev による方法では、カーネルが検知したデバイスだけがデバイスノードとなります。
デバイスノードはシステムが起動するたびに生成されることになるので、 tmpfs
ファイルシステム上に保存されます。 (tmpfs
は仮想ファイルシステムであり、メモリ上に置かれます。)
デバイスノードの情報はさほど多くないので、消費するメモリ容量は無視できるほど少ないものです。
2000年2月に新しいファイルシステム devfs
がカーネル
2.3.46 に導入され、2.4系の安定版カーネルにて利用できるようになりました。
このファイルシステムはカーネルのソース内に含まれ実現されていましたが、デバイスを動的に生成するこの手法は、主要なカーネル開発者の十分な支援は得られませんでした。
devfs
が採用した手法で問題になるのは、主にデバイスの検出・生成・命名の方法です。 特にデバイスの命名方法がおそらく最も重大な問題です。
一般的に言えることとして、デバイス名が変更可能であるならデバイス命名の規則はシステム管理者が考えることであって、特定の開発者に委ねるべきことではありません。
また devfs
にはその設計に起因した競合の問題があるため、根本的にカーネルを修正しなければ解消できる問題ではありません。
そこで長い間、保守されることがなかったために非推奨 (deprecated) として位置づけられ、最終的に
2006年6月にはカーネルから取り除かれました。
開発版の 2.5 系カーネルと、後にリリースされた安定版のカーネル 2.6 系を経て、新しい仮想ファイルシステム
sysfs
が登場しました。 sysfs
が実現したのは、システムのハードウェア設定をユーザー空間のプロセスとして表に出したことです。
ユーザー空間での設定を可視化したことによって devfs
が為していたことを、ユーザー空間にて現実に見ることが可能になったわけです。
sysfs
ファイルシステムについては上で簡単に触れました。
sysfs
はどのようにしてシステム上に存在するデバイスを知るのか、そしてどのデバイス番号が利用されるのか。 そこが知りたいところです。
カーネルに直接組み込まれて構築されたドライバでは、対象のオブジェクトがカーネルによって検出されたものとしてそのオブジェクトを
sysfs
に登録します。
モジュールとしてコンパイルされたドライバでは、その登録がモジュールのロード時に行われます。 sysfs
ファイルシステムが (/sys
に) マウントされると、組み込みのドライバによって sysfs
に登録されたデータは、ユーザー空間のプロセスとデバイスノード生成を行う
udevd にて利用可能となります。
初期起動スクリプト S10udev
は、Linux のブート時にデバイスノード生成を受け持ちます。 このスクリプトは /sbin/hotplug のデフォルトから uevent
ハンドラを取り除きます。 この時点でカーネルは、他の実行モジュールを呼び出す必要がないからです。 そのかわりに、カーネルが起動する
uevent をネットリンクソケット (netlink socket) 上で待ち受けます。 そしてブートスクリプトが
/lib/udev/devices
内にある静的なデバイスノードをすべて /dev
にコピーします。
デバイスやディレクトリ、シンボリックリンクがこの時点で利用可能になっていないと、システム起動の初期段階において動的デバイスを扱う処理が動作しないためです。
あるいは udevd
自身がそれを必要とするからでもあります。 /lib/udev/devices
内に静的なデバイスノードを生成することで、動的デバイスを取り扱うことができないデバイスも動作させることができます。
こうしてブートスクリプトは Udev デーモン、つまり udevd を起動し、それがどのような uevent
であっても対応できるものとなります。 最後にブートスクリプトはカーネルに対して、すべてのデバイスにおいて既に登録されている
uevent を再起動させ、 udevd がそれを待ち受けるものとなります。
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-151/writing_udev_rules/index.html
にあります。
モジュールとしてコンパイルされたデバイスドライバの場合、デバイス名の別名が作り出されています。 その別名は
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-2.6.32.8 においての
Udev は、 INPUT、IDE、PCI、USB、SCSI、SERIO、FireWire
の各デバイスに対するドライバをロードします。 それらのデバイスドライバが適切に構築されているからです。
目的のデバイスドライバが Udev に対応しているかどうかは、 modinfo
コマンドに引数としてモジュール名を与えて実行します。 /sys/bus
ディレクトリ配下にあるそのデバイス用のディレクトリを見つけ出して、 modalias
ファイルが存在しているかどうかを見ることで分かります。
sysfs
に modalias
ファイルが存在しているなら、そのドライバはデバイスをサポートし、デバイスとの直接のやり取りが可能であることを表します。
ただしエイリアスを持っていなければ、それはドライバのバグです。 その場合は Udev
に頼ることなくドライバをロードするしかありません。 そしてそのバグが解消されるのを待つしかありません。
/sys/bus
ディレクトリ配下の対応するディレクトリ内に
modalias
ファイルがなかったら、これはカーネル開発者がそのバス形式に対する modalias のサポートをまだ行っていないことを意味します。
Linux-2.6.32.8 では ISA バスがこれに該当します。 最新のカーネルにて解消されることを願うしかありません。
Udev は snd-pcm-oss のような 「ラッパー (wrapper)」 ドライバや loop のような、現実のハードウェアに対するものではないドライバは、ロードすることができません。
「ラッパー (wrapper)」
モジュールが単に他のモジュールの機能を拡張するだけのものであるなら (例えば snd-pcm-oss は snd-pcm の機能拡張を行うもので、 OSS
アプリケーションに対してサウンドカードを利用可能なものにするだけのものであるため) modprobe
の設定によってラッパーモジュールを先にロードし、その後でラップされるモジュールがロードされるようにします。 これは以下のように
/etc/modprobe.d/
ファイル内にて 「install」 の記述行を加えることで実現します。
<filename>
.conf
install snd-pcm /sbin/modprobe -i snd-pcm ; \
/sbin/modprobe snd-pcm-oss ; true
問題のモジュールがラッパーモジュールではなく、単独で利用できるものであれば、 S05modules
ブートスクリプトを編集して、システム起動時にこのモジュールがロードされるようにします。 これは /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 が異なった名前のデバイスを生成していないことです。
Udev がデバイスノード生成のために必要となる情報を知るためには、カーネルドライバが sysfs
に対して属性データを提供していなければなりません。
これはカーネルツリーの外に配置されるサードパーティ製のドライバであれば当たり前のことです。 したがって /lib/udev/devices
において、適切なメジャー・マイナー番号を用いた静的なデバイスノードを生成してください。 (カーネルのドキュメント
devices.txt
またはサードパーティベンダーが提供するドキュメントを参照してください。) この静的デバイスノードは、S10udev ブートスクリプトによって
/dev
にコピーされます。
これは Udev の設計仕様に従って発生するもので、uevent の扱いとモジュールのロードが平行して行われるためです。 このために命名順が予期できないものになります。 これを 「固定的に」 することはできません。 ですからカーネルがデバイス名を固定的に定めるようなことを求めるのではなく、シンボリックリンクを用いた独自の生成規則を作り出して、そのデバイスの固定的な属性を用いた固定的な名前を用いる方法を取ります。 固定的な属性とは例えば、Udev によってインストールされる様々な *_id という名のユーティリティが出力するシリアル番号などです。 設定例については 7.10. 「デバイスへのシンボリックリンクの生成」 や 7.13. 「ネットワークスクリプトの設定」 を参照してください。
さらに参考になるドキュメントが以下のサイトにあります: