カーネルのソースを読みこなすためには,モジュールの仕組みを知る必要があります。カーネルの多くがモジュールでできているからです。デバイス・ドライバを例に,モジュールをコンパイルする仕組みやカーネルに付加する仕組みを見ていきましょう。
カーネルとデバイス・ドライバは切っても切り離せない関係にあります。
一つの理由は,カーネル・コードの一番多くの部分を占めているのがデバイス・ドライバだという事実です。
もう一つは,デバイス・ドライバをカーネルに組み込む方法を理解することが,カーネル・ソースを読みこなす近道になるということです。カーネルは,「モジュール」という単位でプログラムを追加したり削除したりできます。デバイス・ドライバだけではなく,いろいろなプログラムをモジュールとしてカーネルに組み込めます。特にデバイス・ドライバの起動方法などを理解すると,モジュールの考え方が分かり,カーネルのソースも読みやすくなります。
そこで今回は,カーネルがどうやってモジュールを起動しているのか,デバイス・ドライバに焦点を当てて,その仕組みや起動の様子を見てみましょう。
Linuxが扱うデバイスにはさまざまなものがあります。その中でも皆さんが必ず使っているのがネットワーク・カードではないでしょうか。デバイス・ドライバには,カーネル内部に組み込む方法と,モジュール形式にする方法があります。何らかの理由で取り替えなければならないことになれば,カーネルに組み込んだ場合は再度カーネルをコンパイルし直す必要があります。これでは不便なので,モジュールとしてコンパイルしておき,使用するモジュールの指定を変更するだけで,別のネットワーク・カードに簡単に取り替えられます。
パソコンを起動するとカーネルが起動され,その後,デバイス・ドライバのモジュールがロードされます。このときカーネルは,付加する必要のあるコンパイル済みのモジュールをロードします。どのようなモジュールを起動時にロードするかを指定しているのが,/etc/modprobe.confというファイルです。
図1は,/etc/modprobe.confファイルの記述の一部です。ここでは,米Broadcom社の「tg3」というギガビット・イーサネット・カードが2枚使用されていることが分かります。これを米Intel社の「e100」というファースト・イーサネット・カードに取り替えるには,tg3 をe100に書き換えて再起動するだけで良いのです。
|
図1●modprobeファイルの記述 |
ドライバのソースの場所
ネットワーク・カードのデバイス・ドライバのソース・プログラムは,カーネルのdrivers/netディレクトリにまとめられています。ファイル名からカードを推測できます。
|
通常,ネットワーク・カードのデバイス・ドライバは,カーネルのソースに含まれています。利用できるデバイス・ドライバが新たに加えられるだけでカーネルのバージョンがアップすることもあります。無線LANでは,自分でモジュールを組み込まなければならない場合もあります。
モジュールをカーネルに付加
/etc/modprobe.confファイルに指定しておくと起動時に自動的にカーネルに付加されます。起動後,手動でモジュールをカーネルに付加するには,insmodコマンド,あるいはmodprobeコマンドを実行します。
逆にrmmodコマンドを用いて,動作していないモジュールをカーネルから削除できます。
では,デバイス・ドライバのモジュールがどのようにして,カーネルに付加されるかを見ていきましょう。
デバイス・ドライバのソース・プログラムの最後に,付加の手順を記述する場所があります。このことを知っておくと,カーネル内部のすべてのモジュールを読む際の糸口になります。
図2はtg3.cの場合です。デバイス・ドライバのソースでは,最後の部分でモジュールを付加したり削除したりする設定を書くのが通例です。このとき実行する関数が次の2種類です。
|
図2●デバイス・ドライバ「tg3.c」のソースの一部 |
|
カーネル全体を「module_init」で検索*1すると,すべてのモジュールを付加する部分を確認できます。
この2つの関数は,引数で指定した関数を実行します。
例えば,モジュールを付加するときは,tg3_init()関数が実行されます。カーネルに付加して起動時に最初に実行される関数が,module_init()関数なのです。
この関数は10954行目にありますから,その中で呼んでいる「pci_module_init()」関数のソースを調べると, pci_register_driver()に再定義されていることが分かります。pci_register_driver()のソースは, drivers/pci/pci-driver.cです。その中を見ると,376行目以降でPCIカードを登録する処理を実行していているのが分かります。
モジュールの組み込みに成功すると0を戻さなければなりません。それを知っていると,10954行目のpci_module_init()関数の戻り値が0でなければデバイス登録に失敗し,0ならば成功したことも分かります。