第6章にて Udev パッケージをインストールしました。 このパッケージがどのように動作するかの詳細を説明する前に、デバイスを取り扱うかつての方法について順を追って説明していきます。
Linux システムは一般に、スタティックなデバイス生成方法を採用していました。 この方法では /dev
のもとに膨大な量の (場合によっては何千にもおよぶ) デバイスノードが生成されます。
現実に存在するハードウェアデバイスが存在するかどうかに関わらずです。 これは MAKEDEV スクリプトを通じて生成されます。 このスクリプトからは
mknod
プログラムが呼び出されますが、その呼び出しは、この世に存在するありとあらゆるデバイスのメジャー/マイナー番号を用いて行われます。
Udev による方法では、カーネルが検知したデバイスだけがデバイスノードとなります。
デバイスノードはシステムが起動するたびに生成されることになるので、 devtmpfs
ファイルシステム上に保存されます。 (devtmpfs
は仮想ファイルシステムであり、メモリ上に置かれます。)
デバイスノードの情報はさほど多くないので、消費するメモリ容量は無視できるほど少ないものです。
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
にて利用可能となります。
初期起動スクリプト /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 を実行します。)
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
が利用される際の初期のパーミッションと所有者の情報のままとなります。
モジュールとしてコンパイルされたデバイスドライバーの場合、デバイス名の別名が作り出されています。 その別名は
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-3.5.2 においての Udev
は、INPUT、IDE、PCI、USB、SCSI、SERIO、FireWire の各デバイスに対するドライバーをロードします。
それらのデバイスドライバーが適切に構築されているからです。
目的のデバイスドライバーが Udev に対応しているかどうかは、modinfo
コマンドに引数としてモジュール名を与えて実行します。 /sys/bus
ディレクトリ配下にあるそのデバイス用のディレクトリを見つけ出して、modalias
ファイルが存在しているかどうかを見ることで分かります。
sysfs
に modalias
ファイルが存在しているなら、そのドライバーはデバイスをサポートし、デバイスとの直接のやり取りが可能であることを表します。
ただしエイリアスを持っていなければ、それはドライバーのバグです。 その場合は Udev
に頼ることなくドライバーをロードするしかありません。 そしてそのバグが解消されるのを待つしかありません。
/sys/bus
ディレクトリ配下の対応するディレクトリ内に
modalias
ファイルがなかったら、これはカーネル開発者がそのバス形式に対する modalias のサポートをまだ行っていないことを意味します。
Linux-3.5.2 では 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 が異なった名前のデバイスを生成していないことです。
Udev がデバイスノード生成のために必要となる情報を知るためには、カーネルドライバーが sysfs
に対して属性データを提供していなければなりません。
これはカーネルツリーの外に配置されるサードパーティ製のドライバーであれば当たり前のことです。 したがって /lib/udev/devices
において、適切なメジャー、マイナー番号を用いた静的なデバイスノードを生成してください。 (カーネルのドキュメント
devices.txt
またはサードパーティベンダーが提供するドキュメントを参照してください。) この静的デバイスノードは、udev ブートスクリプトによって /dev
にコピーされます。
これは Udev の設計仕様に従って発生するもので、uevent の扱いとモジュールのロードが平行して行われるためです。 このために命名順が予期できないものになります。 これを「固定的に」することはできません。 ですからカーネルがデバイス名を固定的に定めるようなことを求めるのではなく、シンボリックリンクを用いた独自の生成規則を作り出して、そのデバイスの固定的な属性を用いた固定的な名前を用いる方法を取ります。 固定的な属性とは例えば、Udev によってインストールされるさまざまな *_id という名のユーティリティが出力するシリアル番号などです。 設定例については 7.5.「デバイスへのシンボリックリンクの生成」や 7.2.「全般的なネットワークの設定」を参照してください。
さらに参考になるドキュメントが以下のサイトにあります:
devfs
のユーザー空間での実装方法
//www.kroah.com/linux/talks/ols_2003_udev_paper/Reprint-Kroah-Hartman-OLS2003.pdf
sysfs
ファイルシステム
//www.kernel.org/pub/linux/kernel/people/mochel/doc/papers/ols-2005/mochel.pdf
より詳細なドキュメントへのリンク //www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html