HelloWorldソースコード解説
今回は、以前の記事にて作成したHelloWorldのソースコードについて見ていきます。
hello.c
#include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) { printk(KERN_ALERT "driver loaded\n"); printk(KERN_ALERT "Hello World\n"); return 0; } static void hello_exit(void) { printk(KERN_ALERT "driver unloaded\n"); } module_init(hello_init); module_exit(hello_exit);
まずは上記コードをビルドし、生成されたドライバはどのような動作をするのか確認します。
このドライバはinsmodコマンドなどによりロードされるとsyslogに「driver loaded」と「Hellow World」という文字列を出力します。
# insmod hello.ko # tail -n 2 /var/log/messages Aug 17 05:31:03 localhost kernel: driver loaded Aug 17 05:31:03 localhost kernel: Hello World
tailコマンドはファイルの末尾部分を出力するコマンドです。
-nオプションをつけることで、出力する行数を指定できます。
今度はドライバ「hello」をアンロードします。
# rmmod hello # tail -n 1 /var/log/messages Aug 17 19:07:17 localhost kernel: driver unloaded
アンロードすると、syslogに「driver unloaded」という文字列が出力されます。
では、ソースコードの解説に移ります。
#include <linux/module.h> #include <linux/init.h>
アプリケーションを開発する場合、インクルードするヘッダファイルは「stdio.h」や「string.h」などになると思いますが、それらはユーザー空間で使用するライブラリであり、ドライバ開発ではインクルードしません。
代わりに、「linux/module.h」や「linux/init.h」などをインクルードします。
MODULE_LICENSE("Dual BSD/GPL");
この行はMODULE_LICENSEマクロでドライバのライセンスを定義しています。
この行を記述することによりこのドライバは「GPLである」と明示したことになり、GPL規約に準拠する必要があります。
この行がなくてもコンパイルは通りますが、警告が出力されます。
static int hello_init(void) { printk(KERN_ALERT "driver loaded\n"); printk(KERN_ALERT "Hello World\n"); return 0; }
上記で定義している「hello_init」関数はドライバロード時、つまりinsmodコマンド実行時などに呼び出されるエントリポイントです。
ロード時のエントリポイントの指定は「module_init」マクロで行います。
アプリケーションのエントリポイントは必然的にmain関数となりますが、ドライバの場合は「module_init」マクロでエントリポイントを指定することになります。
戻り値に「0」を返すことで、insmodコマンドは成功します。ロード時の処理で何らかのエラーが発生した場合は0以外を返します。(上記サンプルでは、無条件で「0」を返しています。)
関数内ではprintk関数を呼び出しています。
似た関数としておなじみのprintf関数がありますが、ドライバでは標準ライブラリをインクルードしていないので当然使えません。
というよりそもそも、カーネル空間で動作するドライバはコンソール画面(標準出力)がないので、使いようがありません。
しかし、それではデバックが大変なので代わりに用意されたのがprintk関数です。
- printk関数
- int printk ( const char *fmt, … )
書式指定(fmt)の先頭に「<番号>」を付けると、メッセージのログレベル(優先度)を指定できる。
「include/linux/kernel.h」にマクロ定義。
・KERN_EMERG
システムは使用できない
・KERN_ALERT
即時にアクションを取る必要がある
・KERN_CRIT
かなり憂慮すべき状態
・KERN_ERR
エラー状態
・KERN_WARNING
警告状態
・KERN_NOTICE
異常ではないが、重要な状態
・KERN_INFO
情報
・KERN_DEBUG
デバッグ・メッセージ
printk関数の出力先はカーネルバッファです。
カーネルバッファはdmesgコマンドで参照できます。
また、syslogdやklogdがカーネルバッファをsyslog(/var/log/message)に定期的に出力しているので、syslogからでも確認できます。
static void hello_exit(void) { printk(KERN_ALERT "driver unloaded\n"); }
「hello_exit」関数はドライバアンロード時、つまりrmmodコマンド実行時などに呼び出されるエントリポイントです。
アンロード時のエントリポイントの指定は「module_exit」マクロで行います。
関数の戻り値は不要です。