#author("2016-08-08T15:57:35+09:00","default:afuruta","afuruta")
* Hello World [#p0df494b]
ほぼ何もしない "Hello World" プログラムを作って Linux Kernel に組み込む単純なモジュールの作り方を見ていきます。まだ、デバイスドライバには遠いです。
** ソースコード [#r86823f5]
&ref(hello_world.tar.gz,,添付ファイル); に一式をまとめておきます。C 言語で実行部を、Makefile で構築手順を書きます。Makefile を書かずにコンパイルできるかというと、自分では試したことがないです。煩雑な記述をした割には得るものが無いでしょう。 
*** 実行部 [#h10ff46b]
#code(c,hello_world.c)
*** Makefile [#o9dc2a8d]
#code(Makefile)
** userland アプリ・ケーションと違うところ [#yb45c026]
*** module_init, module_exit [#d8f36181]
main() はありません。モジュールは Kernel に組み込まれる時 &ogdefs(module_init(),module_init,init.h); で指定した関数を実行します。モジュールが Kernel から外されるとき &ogdefs(module_exit(),module_exit,init.h); で指定した関数を実行します。

モジュールが「組み込まれた」、「外された」時に呼ばれる仕掛けを作ることができるのは分りました。デバイス・ドライバを構成するには足りません。「ハードウエアにドライバが対応するデバイスが追加されたら」、あるいは、「モジュールが組み込まれた時点で既にドライバが対応するハードウエアが存在したら」、呼び出される様な仕掛けをまだ作っていません。

*** printk [#w0e5feae]
&ogdefs(printk); は printf に相当する関数です。&ogfileone(書式文字列, Documentation/printk-formats.txt);を指定して書式文字列に続く引数の内容を表示します。書式文字列は printf とよく似ています。興味深く注意が必要な拡張は %p とその派生書式です。ここでは printk のヘッダファイル読み込みに &ogfileone(linux/kernel.h,include/linux/kernel.h); を使用しました。もちろん、&ogfileone(linux/printk.h,include/linux/printk.h); を直接インクルードするのも良いでしょう。
#textbox(note,printk に浮動小数点形式の書式が無いようだが...){{
printkの書式に浮動小数点形式はありません。kernel の中では double, float とその派生型は使えません。自分は実施したことが無いのですが、浮動小数点レジスタの値とこれに関係するコンテキストを保存・復帰すれば使えるはずです。ただし、処理の出入り口に実装する程度の簡単なことではなく、suspend/resume、コンテキスト・スイッチ、プロセッサ active/idle の状況などを細かに意識する必要が有ります。実装に問題があるとアプリケーションで行っている計算結果が不意にとんでもない値になるなど問題追跡が難しい状況に陥ります。
}}
*** __init, __exit [#f0e4c8af]
関数に &ogdefs(__init,__init,linux/init.h); と &ogdefs(__exit,__exit,linux/init.h); 修飾子がついています。 &ogfileone(include/linux/init.h); を読んでみると、広域変数に対して似たようなマクロ定義があることが判ります。&ogfiledirect(__init と __exit 修飾子の目的は, Documentation/DocBook/kernel-hacking/routines-init.html); 次のようになります。

- __init: モジュールをロードするときに呼び出す関数を修飾します。より厳密に言えば &ogdefs(module_init(),module_init,init.h); で指定した関数から呼び出し関係を辿れて、辿った経路以外からの呼び出しが無い関数を修飾します。__init が付いた関数はモジュール・ロード(組み込み)が終了し module_init() からの呼び出しが完了した時点でメモリから削除されます。
- __exit: モジュールをアンロードする時に呼び出す関数を修飾します。より厳密に言えば &ogdefs(module_exit(),module_exit,init.h); で指定した関数から呼び出し関係を辿れて、辿った経路以外からの呼び出しが無い関数を修飾します。(このページでは取り上げないのですが)モジュールを Kernel に静的に結合した場合、モジュール・アンロードは起きません。__exit を付けた関数は Kernel のオブジェクトコードから除去されます。 

__init と __exit は Kernel のメモリ使用量をなるべく減らすために使われる修飾子です。
*** KERN_INFO [#ac85fd7f]
&ogdefs(KERN_INFO,KERN_INFO,linux/kern_levels.h); は kernel message の表示レベルを意味します。&ogfileone(include/linux/kern_levels.h); には次のレベルが定義されています。おそらく普通に使うのは KERN_ERR より大きい Level 番号のメッセージでしょう。

|Macro|Level|alternate function|alternate function&br;for device driver|Description|h
|&ogdefs(KERN_EMERG,KERN_EMERG,linux/kern_levels.h);|CENTER:0|&ogdefs(pr_emerg,pr_emerg,printk.h);|&ogdefs(dev_emerg,dev_emerg,device.h);|system is unusable|
|&ogdefs(KERN_ALERT,KERN_ALERT,linux/kern_levels.h);|CENTER:1|&ogdefs(pr_alert,pr_alert,printk.h);|&ogdefs(dev_alert,dev_alert,device.h);|action must be taken immediately|
|&ogdefs(KERN_CRIT,KERN_CRIT,linux/kern_levels.h);|CENTER:2|&ogdefs(pr_crit,pr_crit,printk.h);|&ogdefs(dev_crit,dev_crit,device.h);|critical conditions|
|&ogdefs(KERN_ERR,KERN_ERR,linux/kern_levels.h);|CENTER:3|&ogdefs(pr_err,pr_err,printk.h);|&ogdefs(dev_err,dev_err,device.h);|error conditions|
|&ogdefs(KERN_WARNING,KERN_WARNING,linux/kern_levels.h);|CENTER:4|&ogdefs(pr_warn,pr_warn,printk.h);, &ogdefs(pr_warning,pr_warning,printk.h);|&ogdefs(dev_warn,dev_warn,device.h);|warning conditions|
|&ogdefs(KERN_NOTICE,KERN_NOTICE,linux/kern_levels.h);|CENTER:5|&ogdefs(pr_notice,pr_notice,printk.h);|&ogdefs(dev_notice,dev_notice,device.h);|normal but significant condition|
|&ogdefs(KERN_INFO,KERN_INFO,linux/kern_levels.h);|CENTER:6|&ogdefs(pr_info,pr_info,printk.h);|&ogdefs(dev_info,dev_info,device.h);|informational|
|&ogdefs(KERN_DEBUG,KERN_DEBUG,linux/kern_levels.h);|CENTER:7|&ogdefs(pr_debug,pr_debug,printk.h);, &ogdefs(pr_devel,pr_devel,printk.h);|&ogdefs(dev_dbg,dev_dbg,device.h);|debug-level messages&br;pr_debug, pr_devel, dev_dbg は DEBUG, CONFIG_DYNAMIC_DEBUG マクロにより、実装したコードそのものが削除されたり、動的に制御される対象になります。リンク先を参照して条件を確認してください。|

#textbox(note,KERN_XXX は行の先頭で有効です){{
KERN_XXX は行の先頭にあるものが有効です。printk を何回か使い、行を分割して出力した場合に、途中に KERN_XXX を入れても効果がありません。途中に誤って入れても、致命的な問題は起こしません。表示やログファイルの内容が乱れるだけです。
}}

&ogfiledirect(/proc/sys/kernel/printk リンク先の printk 節を参照,Documentation/sysctl/kernel.txt); にログレベルを書き込むことで console と ログファイル(厳密にはログ出力ノード /proc/kmsg) に出力するレベルを制御できます。説明の中で "Higher" とあるのは上記表 Level においてより小さい値です。

** Linux Kernel に動的に組み込むモジュールとして作成する [#rafbd0e4]
*** Makefile を書く [#kea9666f]
** Linux Kernel に組み込む [#i3d8ce65]
*** 何も起きない? dmesg で確かめよう [#v37a74c8]
*** printk は何処と結合しているの? [#g9da7396]
*** 多重に組み込めない [#c6e7352f]
* Hello Friend [#x7542c70]
** パラメータを入力する [#a8504c49]
** エラーを返してみよう [#fca65edf]

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS