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