Document

Linux Device Driver 輪講
2. モジュールの作成と実行
ACE suzuk
table of contents
1.
2.
3.
4.
5.
6.
7.
8.
9.
テスト用システムのセットアップ
Hello Worldモジュール
モジュール対アプリケーション
コンパイルとロード
カーネルシンボルテーブル
準備情報
初期化とシャットダウン
モジュールパラメータ
ユーザ空間を使う
1.テスト用システムのセットアップ
 Linuxドライバ実装&テスト環境を構築
 2.6からはシステム上にカーネルツリーが必要
 kernel.orgから2.6カーネルソースをDLし、
/usr/src以下にインストールする
 実行カーネルと同じバージョンのカーネルソースを
インストールするのが望ましい
2.Hello Worldモジュール
 2つの関数に注目
 module_init (カーネルマクロ)
 モジュールのカーネルにロード時に実行
 module_exit (カーネルマクロ)
 モジュールのカーネルからの削除時に実行
 printk
 Cライブラリなしに実行するためのprintfに似た関
数
モジュールがprintkを呼び出せるのはなぜ?
insmodでロードするとモジュールがカーネルにリンクされ、
カーネルのパブリックシンボルにアクセスできるようになるため
printkについてもう少し・・・
 i.e. printk(KERN_ALERM “hello\n”);
 KERN_ALERM
 メッセージの重要度
3.モジュール対アプリケーション
 カーネルモジュールは全てイベントドリブン
 終了関数は初期化関数で作成したオブジェクトを全て注意
深く消去する必要がある
 システムリブートまで資源を解放できなくなるため
 モジュールはカーネルだけにリンクされる
 呼び出せる関数はカーネルがエクスポートしたもの
 リンクすべきライブラリがない!
 通常のヘッダファイルをインクルードしてはだめ!
 ライブラリがリンクされないため(stdarg.hはOK)
 カーネルヘッダ
 include/linux, include/asm
 カーネルサブシステムヘッダ
 上記ディレクトリ以外のサブディレクトリ
 カーネルモジュールの深刻なエラーはシステム全体
を巻き込む
カーネルモジュールの役目
 カーネルの機能を拡張すること
 システムコールの一部として実行される
 割り込み処理を担当する
 カーネル空間で実行される
ユーザ空間とカーネル空間
 カーネルモードとユーザモード
 セキュリティと安全性のため異なる特権状態で命
令を実行する
 異なるCPUのランレベルを利用する
 Linuxでは特権モードと非特権モードの2つを使い
分ける
カーネルモード
 あらゆるハードウェア資源にアクセス可能
 オペレーティングシステムの実行モード
プロセス
プロセス
プロセス
ユーザモード
システムコール
カーネルモード
カーネル
デバイス
ユーザモード
 ハードウェア資源へのアクセスを制限・監視下
でプログラムを実行
 通常のプログラムの実行モード
プロセス
プロセス
プロセス
ユーザモード
システムコール
カーネルモード
カーネル
デバイス
CPUのランレベル
 多くのCPUは2つ以上の実行モードを保有
 実行レベルを使い分けることで安全性、安定性を
向上させる
 Intel 80x86は4つの実行リング(特権の階層)を
持つ
ランレベル0をカーネルモード
それ以上をユーザモードに割り当てる
カーネル内の並行処理
 常に並行処理を意識した実装が必要
 複数プロセスがドライバを同時に使用した時
 デバイスの非同期の割り込み実行
 ソフトウェア割り込み実行
 カーネルタイマ(7章)
 マルチプロセッサ
 プリエンプティブ対応のリエントラントな実装
 同時に複数のコンテキストで実行できなくてはだめ
 共有データの安全性
 競争状態の回避(実行順序の依存性をなくす)
カレントプロセス
 カーネルモジュールは、順々に実行されない
 ユーザプロセスと違う!
 カーネルの多くのアクションは個々のプロセス
に関連付けられる
 currentグローバル変数でカレントプロセス情報を
利用する
 asm/current.h, linux/sched.hを参照のこと
 linux/sched.hをincludeして参照できる
printk(KERN_INFO “process is %s (pid:%i)\n”, current->comm,
current->pid);
カーネルの海路に旅立つあなたへ
 心に留めておくべき事柄
 カーネルのスタックは非常に小さく、スタックを共
有する必要がある
 大きな自動変数は良くない!
 大きな構造体の場合動的割り当てにする
 __(ダブルアンダースコア)関数の使用に注意
 インタフェースの下位レベルコンポーネント
 浮動小数点の計算ができない
4.コンパイルとロード
 カーネルソースはコンパイラに関して非常に多
くの仮定を持つ(コンパイラ依存)
 Documentation/Changesに必要ツールと
バージョンのリスト
 全てを知りたい
 Documentation/kbuild以下を読む
モジュールのロードと削除
 insmod (モジュールのロード)
 モジュールコードとデータをカーネルにロード
 モジュール内の未解決シンボルをカーネルのシン
ボルテーブルとリンクする
 ファイルでなくメモリイメージを修正する点がリンカと
違う
 全てを知りたい人kernel/module.cを参照!
 sys_init_module(systemcallには先頭がsys_)
 modprobe
 他のモジュール依存を解決してからロード
 rmmod
 モジュールをカーネルから削除
helloworldのコンパイルとロード
 Makefile
Makefileはサンプルを参照のこと
 obj-m := helloworld.o
 コンパイル
 make -C /usr/src/linux-2.6.16 M=`pwd`
modules
 ロードと削除
 insmod ./helloworld.ko
 rmmod ./helloworld.ko
 結果
 /var/log/messages
 May 3 05:07:48 localhost kernel: Hello, world
 May 3 05:07:58 localhost kernel: Goodby, cruel
world
カーネルのバージョンへの依存性
 カーネルのバージョンが変わるたびに再コン
パイルする必要がある
 モジュールから見たカーネルインタフェースはバー
ジョン毎にかなり変更される
 複数バージョンに対応するには
 #ifdefとマクロを活用する
5.カーネルシンボルテーブル
 グローバルなカーネルアイテムのアドレステー
ブル
 関数や変数のテーブル
 ロードされたモジュールのエクスポートされた
シンボルは全てカーネルシンボルテーブルに
追加される
 通常はエクスポートする必要はない
 モジュールを積み重ねる(stack)こともできる
 USB入力デバイスモジュールはusbcoreとinput
モジュールに積み重ねられる
 スタックされたモジュールにはmodprobeが便利
 インストール済みモジュールだけ探索することに注意
5.カーネルシンボルテーブル 2
 シンボルをエクスポート
 宣言
 EXPORT_SYMBOL(name);
 EXPORT_SYMBOL_GPL(name);
 モジュールのELFセクションにストアされる
 全てを知りたい人はlinux/module.h
ポートの共有と
デバイスの登録
下位レベル
のデバイス操作
parport_pc
parport
lp
※パラレルポートドライバvモジュールのスタックの様子
カーネルAPI
(メッセージ出力、
ドライバ登録、
ポート割り当てなど)
6.準備情報
 全てのモジュールで必要なヘッダ
 linux/module.h
 シンボルと関数の定義
 linux/init.h
 初期化とクリーンアップの関数指定
 その他のヘッダ
 moduleparam.h
 モジュールロードにおけるパラメータ指定
 ライセンス指定
 MODULE_LICENSE(“GPL”); マクロで!
 フリーライセンス指定しないとロード時にカーネル
が汚染されたと表示される
7.初期化とシャットダウン
 module_init(initfunc_name)
 __init
initfunc_name宣言
 __initは初期化の際に使用されるものと宣言
 __initdataタグ
 初期化の間だけ使用されるデータ
 module_exit(cleanupfunc_name)
 __exit cleanupfunc_name
 __exitはクリーンアップの際に使用されるものと宣言
 facilityの登録
 モジュールは多くの異なるfacilityを登録できる
 装備には、facilityごとに特定の登録カーネル関
数がある
初期化関数におけるエラー処理
 常に戻り値をチェックしたエラー処理が必要
 モジュールロードを継続できない場合は、そこまで
の登録を取り消す
 エラー処理にはgoto文が有効
モジュールロード時の競争
 facilityをサポートする内部の初期化手続き
が完了するまで、そのfacilityを登録しない
 初期化関数の実行が終了しない状態で、カーネル
がモジュールを呼び出す可能性がある
 初期化が決してエラーにならないようにする
8.モジュールパラメータ
 ロード時にinsmodかmodprobeで指定でき
る
 modprobeは/etc/modprobe.confからパラ
メータを読み取る
 module_param, module_param_arrayマク
ロを使う(moduleparam.hで定義)
static char *whom = “world”;
static int howmany = 1;
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO);
9.ユーザ空間を使う
 ユーザ空間ドライバの利点






Cライブラリをリンクできる
整備されたデバッガでデバックできる
ハングしてもシステム全体に影響しない
スワップできる(RAMを占有しない)
デバイスの同時アクセスを許可できる
ライセンスやカーネルインタフェースの変更問題をユーザ空間の
オプションによって回避できる
 ユーザ空間ドライバを書く場合・・・
 サーバプロセスをつくり、ハードウェアを制御する唯一の仕事人
の役割をカーネルから引き継ぐ
Xサーバ
9.ユーザ空間を使う 2
 ユーザ空間ドライバの欠点
 割り込みを使えない
 IA32では、vm86システムコールがある
 メモリへの直接アクセスが/dev/memに対する
mmapシステムコールのみしかない
 I/Oポートへのアクセスは、iopermかiopl呼び出
し後にしか行えない
 反応に時間がかかる
 ディスクスワップされている場合は特に!
 ネットワークインタフェースやブロックデバイスなど
はユーザ空間で扱えない
 新しいデバイスを扱う場合は、まずはユーザ
空間で実装するのも良い