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呼び出 し後にしか行えない 反応に時間がかかる ディスクスワップされている場合は特に! ネットワークインタフェースやブロックデバイスなど はユーザ空間で扱えない 新しいデバイスを扱う場合は、まずはユーザ 空間で実装するのも良い
© Copyright 2024 ExpyDoc