#author("2017-09-15T10:29:21+09:00","default:afuruta","afuruta")
#author("2017-10-15T23:59:55+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(txt,Makefile);
** hello_world.c が user space アプリケーションと違うところ [#yb45c026]
C 言語で Linux application の hello world プログラムを書いた場合と違うところを見て行きます。おそらく「知っている」C 言語のプログラムと違う点は「これで動くのだろうか?」と思えてくるでしょう。
*** module_init, module_exit [#d8f36181]
main() はありません。モジュールが kernel に組み込まれる時 &ogdefs(module_init(),module_init,init.h); で指定した関数を実行します。モジュールが kernel から外されるとき &ogdefs(module_exit(),module_exit,init.h); で指定した関数を実行します。

#code(c,"/define\s*module_init/..=-8+12",ogfileone:/include/linux/init.h);

モジュールが「組み込まれた」、「外された」時に呼ばれる仕掛けを作ることができるのは分りました。しかし、デバイス・ドライバを構成するにはまだ足りません。「ハードウエアにドライバが対応するデバイスが追加されたら」、あるいは、「モジュールが組み込まれた時点で既にドライバが対応するハードウエアが存在したら」、呼び出される様な仕掛けをまだ作っていません。[[i8253 PIT をアクセスするデバイスドライバ]]で probe, remove と呼ばれるデバイスとドライバを関係づける「メソッドのような関数」を見ていきます。

*** __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 のメモリ使用量をなるべく減らすために使われる修飾子です。

#code(c,/define\s*__init/../__initconst/,ogfileone:/include/linux/init.h);
#code(c,/define\s*__exit\s*__section/..+1,ogfileone:/include/linux/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 の状況などを細かに意識する必要が有ります。実装に問題があるとアプリケーションで行っている計算結果が不意にとんでもない値になるなど問題追跡が難しい状況に陥ります。
}}
*** KERN_INFO [#ac85fd7f]
&ogdefs(KERN_INFO,KERN_INFO,linux/kern_levels.h); は kernel message の表示レベルを意味します。&ogfileone(include/linux/kern_levels.h); には次のレベルが定義されています。おそらく普通に使うのは KERN_ERR より大きい Level 番号のメッセージでしょう。下の表の alternate function pr_xxx は printk に KERN_XXX マクロを組み合わせて定義したマクロです。デバイス・ドライバ向けに dev_xxx 関数も用意されています。dev_xxx はデバイスの場所や関連するコンテキストが格納された &ogdefs(struct device,device,linux/device.h); を指すポインタを引数に渡します。

|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 マクロにより、実装したコードそのものが削除されたり、&ogfileone(/sys/kernel/debug/dynamic_debug/contol を通して動的に制御できる,Documentation/dynamic-debug-howto.txt); ようになります。|

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

#textbox(caution,/sys/kernel/debug の下に何もない){{
Linux ディストリビューションあるいは SoC メーカーが提供している Linux 環境によっては /sys/kernel/debug の下にノードが全くない場合が有ります。&ogfileone(debugfs,Documentation/filesystems/debugfs.txt); (&ogfileone(debugfs の kernel 内ヘッダ,include/linux/debugfs.h);)というデバッグ専用のファイル・システムをマウントしてください。&span(ConsoleOut){$ };&span(ConsoleIn){sudo mount -t debugfs none /sys/kernel/debug}; でマウントします。
}}

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

別のページで各種のデバッグ手法について集中的に説明する予定です。
*** MODULE_LICENSE [#p712afbe]
&ogdefs(MODULE_LICENSE,MODULE_LICENSE,include/linux/module.h); はモジュールのライセンスを指定します。このライセンスは &ogfiledirect(シンボルの結合規則,Documentation/DocBook/kernel-hacking/symbols.html);に関係しています。モジュールが結合可能な kernel symbol は &ogdefs(EXPORT_SYMBOL,EXPORT_SYMBOL,include/linux/export.h); または &ogdefs(EXPORT_SYMBOL_GPL,EXPORT_SYMBOL_GPL,include/linux/export.h); によって kernel から export されています。MODULE_LICENSE マクロの引数に指定する license 文字列と結合可能なシンボルは次の通りです。

|license 文字列|モジュールから結合可能な kernel symbol export 方法|h
|"GPL", "GPL v2", "GPL and additional rights", "Dual BSD/GPL", "Dual MIT/GPL",  "Dual MPL/GPL"|EXPORT_SYMBOL, EXPORT_SYMBOL_GPL|
|Proprietary|EXPORT_SYMBOL|

ライセンスの宣言通り、ソースコードを配布できるようにする必要が有ります。

モジュールのソースを公開したくなければ "Proprietary" を指定します。このようにすると、結合可能なシンボルは限られます。開発段階で "GPL"を指定し、リリース直前に "Proprietary" を指定すると結合できないシンボルが出てきて、実装し直しあるいはライセンス変更と言った混乱が発生します。

#textbox(note,kernel に静的に結合する場合){{
kernel に静的に結合する場合は結合先のシンボルが EXPORT_SYMBOL または EXPORT_SYMBOL_GPL で宣言されていなくても結合できます。ただし、結合させようとするコードのライセンスは GPL または GPL ライセンスのコードと(&ogfileone(結合可能と見なされるライセンス include/linux/module.h 参照,include/linux/module.h);)でリリースする必要が有ります。
}}

*** MODULE_DESCRIPTION, MODULE_AUTHOR [#mdfbd846]
&ogdefs(MODULE_DESCRIPTION,MODULE_DESCRIPTION,include/linux/module.h);, &ogdefs(MODULE_AUTHOR,MODULE_AUTHOR,include/linux/module.h); はそれぞれモジュールの説明、モジュールの作者を設定します。 &ogdefs(MODULE_AUTHOR,MODULE_AUTHOR,include/linux/module.h); は、フルネーム、メールアドレス、会社名などを入れます。ソース・コードに現れる例を参考に設定して下さい。これらの設定はモジュールの動作に影響を与えません。設定内容は次のコマンドで確かめることができます。
#pre(soft){{
&span(ConsoleOut){$ }; &span(ConsoleIn){/sbin/modinfo };&span(ConsoleIn,Alias){module_name_or_module_path};
}}
** Makefile [#x4106800]
&ogfileone(module を構築するための Makefile の書き方 modules.txt, Documentation/kbuild/modules.txt); に詳細が書かれています。ここでは、1 個のソース・ファイルから構築しているのでより簡単な Makefile にしました。Linux kernel のソース・コードからバイナリを構築する Makefile の仕組みの上でビルドします。make の -C オプションでディレクトリを Linux kernel のソース・コード・ツリーに移して M マクロにビルドするディレクトリのパスを設定します。
#textbox(note,/lib/modules/$(shell uname -r)/build はどこのディレクトリ?){{
/lib/modules/$(shell uname -r)/build は今使っている kernel を構築したソース(またはその中から構築に必要なファイルだけを残した)ディレクトリ・ツリーをシンボリック・リンクで指しています。手順通り進めていたならば、構築作業をしたばかりのソース・コードツリーを指しています。
}}

** Linux kernel に動的に組み込むモジュールとして作成する [#rafbd0e4]
&ref(hello_world.tar.gz,,添付ファイル);を展開したディレクトリに移って、make します。
#pre(soft){{
&span(ConsoleOut){$ };&span(ConsoleIn){cd };&span(ConsoleIn,Alias){extracted_directory_base};&span(ConsoleIn){/hello_world};
&span(ConsoleOut){$ };&span(ConsoleIn){make};
}}
特に驚くような警告やエラーは出ないはずです。最終的なターゲットは hello_world.ko です。Linux ドライバを提供しているハードウエアの中には、すべて Makefile で書くのではなく、shell script で make を呼び出すものもあります。shell scripit の中で次の様なコマンドラインを実行する様に書かれています。
#pre(soft){{
&span(ConsoleOut){make -C /lib/modules/};&span(ConsoleOut,Alias){4.1.27-local};&span(ConsoleOut){/build M=};&span(ConsoleOut,Alias){/home/furuta/git/lkdq1stmodule};&span(ConsoleOut){ modules};
}}

** Linux kernel に組み込む [#i3d8ce65]
make コマンドに続けて次のようにしてモジュールを組み込みます。何か起きましたか?
#pre(soft){{
&span(ConsoleOut){$ };&span(ConsoleIn){sudo insmod hello_world.ko};
&span(ConsoleOut){[sudo] password for };&span(ConsoleOut,Alias){user_name};&span(ConsoleOut){:};&span(ConsoleIn,Alias){password};
}}
*** 何も起きない? dmesg で確かめよう [#v37a74c8]
コンソールには insmod コマンドに反応した表示は何も出ないはずです(組み込み Linux 環境ならば kernel message がコンソールに流れるように設定されている場合もあります)。プログラムに書かれた "Hello world." が出力されたのかは dmesg コマンドで確かめることができます。
#pre(soft){{
&span(ConsoleOut){$ };&span(ConsoleIn){dmesg};
...省略...
&span(ConsoleOut){[  910.921955] hello_world: module verification failed: signature and/or required key missing - tainting kernel};
&span(ConsoleOut){[  910.921955] hello_world_init: Hello world.};
}}
左側に表れている数値は printk timestamp または printk time と呼ばれているものです。正式な名称は Documentation、ソース・コードの中に書かれていないようです。nano second 単位の &ogdefs(local_clock(), local_clock); 関数から得ている時刻です。
#textbox(note,小数点付で時刻が表示されている){{
小数点付で時刻が表示されています。しかし、この表示のために浮動小数点演算はしていません。printk の表示は整数計算で行っています。&ogdefs(print_time(),print_time,printk.c); がprintk time 表示処理です。整数計算で &ogdefs(do_div(), do_div); を使っています。kernel の中では long を超える語長の除算は / と % が使えない場合があります。アプリケーション・プログラムをコンパイルするときはプロセッサに適した命令が無い場合、演算子に対して自動的に計算するライブラリが結合します。しかし、Linux kernel では演算子に対してライブラリを自動的に結合せず、商と余りを計算するマクロまたは関数を提供します。
}}
#code(c,/static.*print_time/../^}$/,ogfileone:/kernel/printk/printk.c);
** Linux kernel に組み込まれたことを確認する [#db7282e1]
モジュールが kernel に組み込まれていることを lsmod コマンドで確認します。下線を付けて示した行が見つかると思います。
#pre(soft){{
&span(ConsoleOut){$ };&span(ConsoleIn){lsmod};
&span(ConsoleOut){Module                  Size  Used by};
&span(ConsoleOut,Focus){hello_world            16384  0};
&span(ConsoleOut){rfcomm                 69632  0};
&span(ConsoleOut){bnep                   24576  2};
&span(ConsoleOut){bluetooth             516096  10 bnep,rfcomm};
&span(ConsoleOut){nfsd                  299008  13};
&span(ConsoleOut){autofs4                40960  4};
&span(ConsoleOut){auth_rpcgss            61440  1 nfsd};
&span(ConsoleOut){nfs_acl                16384  1 nfsd};
&span(ConsoleOut){nfs                   249856  0};
&span(ConsoleOut){lockd                  94208  2 nfs,nfsd};
&span(ConsoleOut){grace                  16384  2 nfsd,lockd};
&span(ConsoleOut){sunrpc                327680  19 nfs,nfsd,auth_rpcgss,lockd,nfs_acl};
&span(ConsoleOut){fscache                65536  1 nfs};
&span(ConsoleOut){snd_intel8x0           40960  2};
&span(ConsoleOut){snd_ac97_codec        135168  1 snd_intel8x0};
&span(ConsoleOut){ac97_bus               16384  1 snd_ac97_codec};
&span(ConsoleOut){snd_pcm               106496  2 snd_ac97_codec,snd_intel8x0};
&span(ConsoleOut){snd_seq_midi           16384  0};
&span(ConsoleOut){snd_seq_midi_event     16384  1 snd_seq_midi};
&span(ConsoleOut){snd_rawmidi            32768  1 snd_seq_midi};
&span(ConsoleOut){snd_seq                69632  2 snd_seq_midi_event,snd_seq_midi};
&span(ConsoleOut){joydev                 20480  0};
&span(ConsoleOut){snd_seq_device         16384  3 snd_seq,snd_rawmidi,snd_seq_midi};
&span(ConsoleOut){snd_timer              32768  2 snd_pcm,snd_seq};
&span(ConsoleOut){snd                    86016  11 snd_ac97_codec,snd_intel8x0,snd_timer,snd_pcm,snd_seq,snd_rawmidi,snd_seq_device};
&span(ConsoleOut){serio_raw              16384  0};
&span(ConsoleOut){soundcore              16384  1 snd};
&span(ConsoleOut){8250_fintek            16384  0};
&span(ConsoleOut){i2c_piix4              24576  0};
&span(ConsoleOut){mac_hid                16384  0};
&span(ConsoleOut){parport_pc             32768  0};
&span(ConsoleOut){ppdev                  20480  0};
&span(ConsoleOut){lp                     20480  0};
&span(ConsoleOut){parport                45056  3 lp,ppdev,parport_pc};
&span(ConsoleOut){hid_generic            16384  0};
&span(ConsoleOut){usbhid                 53248  0};
&span(ConsoleOut){hid                   118784  2 hid_generic,usbhid};
&span(ConsoleOut){psmouse               122880  0};
&span(ConsoleOut){ahci                   36864  5};
&span(ConsoleOut){libahci                32768  1 ahci};
&span(ConsoleOut){e1000                 139264  0};
&span(ConsoleOut){pata_acpi              16384  0};
}}
** Linux kernel から外す [#c957a200]
rmmod コマンドでモジュールをアンロードしない限り、Linux kernel 内にモジュールは留まります。hello_world.c に留まった状態を確認できるような機能を実装したいところですが、まだ先になりそうです。rmmod コマンドを実行します。rmmod コマンドの引数はモジュール名です。.ko を付けていません(付けてもエラーになりません)。
#pre(soft){{
&span(ConsoleOut){$ };&span(ConsoleIn){sudo rmmod hello_world};
}}
続けて、dmesg で確かめてみましょう。module_exit() で指定した関数 hello_world_exit() が実行されているのが分かります。
#pre(soft){{
&span(ConsoleOut){$ };&span(ConsoleIn){dmesg};
...省略...
&span(ConsoleOut){[  910.921955] hello_world: module verification failed: signature and/or required key missing - tainting kernel};
&span(ConsoleOut){[  910.922372] hello_world_init: Hello world.};
&span(ConsoleOut){[  933.824630] hello_world_exit: Goodbye world.};
}}
lsmod コマンドをもう一度し hello_world モジュールが外されているのを確かめてみてください。
#textbox(note,メモリアロケートをした場合はどうなるの?){{
モジュールでメモリアロケートをするのはまだ先の話です。モジュールで確保したメモリは rmmod で自動的に解放されません。デバイスが remove されたとき、module_exit()で指定した関数が実行されたときにメモリ開放処理をする様に書きます。あるいは、メモリを開放できない状況では module_exit() が実行されないようにします。モジュールの参照カウンタを増加・減少させる関数 &ogdefs(try_module_get(), try_module_get);, &ogdefs(module_put(),module_put); を参照してください。 
モジュールでメモリアロケートをするのはまだ先の話です。モジュールで確保したメモリは rmmod で自動的に解放されません。デバイスが remove されたとき、module_exit()で指定した関数が実行されます。module_exit() に指定した関数の処理でメモリ開放をする様に書きます。あるいは、メモリを開放できない状況では module_exit() が実行されないようにします。モジュールの参照カウンタを増加・減少させる関数 &ogdefs(try_module_get(), try_module_get);, &ogdefs(module_put(),module_put); を参照してください。 
}}
*** モジュールは一つだけ組み込める [#c6e7352f]
同じモジュールは 1 つだけ kernel に組み込めます。2 つ組み込めないか試してみましょう。2 回目は "File exists" (&ogdefs(EEXIST, EEXIST, errno-base.h);, 発生源は &ogdefs(add_unformed_module(),add_unformed_module); 辺りかな) というエラーになります。
#pre(soft){{
&span(ConsoleOut){$ };&span(ConsoleIn){sudo insmod hello_world.ko};
&span(ConsoleOut){$ };&span(ConsoleIn){sudo insmod hello_world.ko};
&span(ConsoleOut){insmod: ERROR: could not insert module hello_world.ko: File exists};
}}
この結果から分ることは何でしょうか?

複数の同一デバイスを N 個接続した場合、1 個のモジュールは N 個のデバイスを扱う構造になります。モジュールのインスタンスがデバイスの数に合わせて作られることは有りません。アプリケーション・プログラムがシステム上に同時に複数存在できるのとは大きな違いです。同一デバイスが追加されるたびにモジュールはそれに対応するインスタンス(状態保持情報)を自ら作り「ユーザー・ランドからの要求を処理する」、「デバイスの変化を処理する」などの事象に応じてインスタンスの情報を更新します。

#textbox(note,どのデバイス・ドライバも同一のデバイスが複数接続された場合に対応しているのか?){{
答えは No です。プラットホーム固有のデバイス(あるいは SoC の IP ブロック)を扱うドライバはシステム内に特定個数のデバイスのみ存在すると仮定して作られることが多いです。任意の個数に対応するように書いたところで、ハードウエアで固定された有限個のデバイスしかないので実装した機能が存分に発揮されません。
}}
** module verification failed: signature and/or required key missing - tainting kernel [#re9fdc6d]
「kernel を汚した」というメッセージが気になります。このメッセージは 1 回だけ出ます。&ogdefs(load_module(), load_module, module.c); 関数で表示しています。「1 回だけ」と言う挙動は &ogdefs(pr_notice_once(),pr_notice_once); で実現しています。気になるのであれば、モジュールに署名を付けましょう。自前で構築したカーネルですのでソース・ツリーに秘密キーが有ります。署名を付けることができます。詳細は &ogfileone(Documentation/module-signing.txt, Documentation/module-signing.txt); を参照して下さい。
#pre(soft){{
&span(ConsoleOut){$ };&span(ConsoleIn){keydir=/lib/modules/`uname -r`/build};
&span(ConsoleOut){$ };&span(ConsoleIn){$keydir/scripts/sign-file sha512 $keydir/signing_key.priv $keydir/signing_key.x509 hello_world.ko};
}}

** いくつかの疑問と希望 [#b2cd7dc7]
hello_world モジュールは何もしないプログラムでした。ここで、こちらから疑問と希望を出しておきましょう。
*** printk は何処と結合しているの? [#g9da7396]
hello_world モジュールは printk 関数を使用しました。この関数のリンク先はまさに稼働している kernel の中のコードです。アプリケーション・プログラムの様に glibc ライブラリが(論理的には)プロセスそれぞれに独立してリンクされるのとは違います。リンクした関数や変数を通して kernel の中の状態を変更すれば、モジュールが外された後もその変更は残り続けます。
*** 引数は受け取れないのか [#n042d5b9]
module_init() で指定した関数の引数は void でした。何も引数らしき値は受け取れないのでしょうか? &ogdefs(module_param(), module_param, moduleparam.h); という特別なマクロと共に変数を使うと引数(そしてより柔軟なパラメータ)を扱うことができます。 
*** エラーは返せないのか? [#tfc52d6b]
module_init() で指定した関数は int 型の値を返す関数です。0 以外の値を返せます。&ogfile(errno-base.h);, &ogfile(errno.h); にあるシステム・コール・エラー番号を負の値にして返すことができます。これで、ユーザー・ランドにエラーが起きたことを伝えることができます。

module_exit() で指定した関数は void 型でした。モジュールを外すときエラーは許されないのでしょうか?答えは「許されない」です。しかし、どうしても無理がある場合は、後で悪いことが起きない程度に後始末します。外す段になって「エラー」だと状況を伝えたところで、大抵は巻き戻して「無かったこと」にするのは無理な場合が多いです。あらかじめモジュールを外せない状況に在ると判っているならば、モジュールの参照カウンタを加減する &ogdefs(try_module_get(), try_module_get);, &ogdefs(module_put(),module_put); を使って下さい。

#textbox(note,終了処理全般の戻り値はおおよそ void){{
Linux kernel 内で、Call Back や 関数テーブル経由(メソッド Call)で呼ばれる終了処理の殆どは void 型の関数です。「これ以上処理することが無く、終わると言うのに、失敗だと言っても仕方がない。」という信条が在るようです。
}}

** HelloWorld を修正する [#s74f31f6]
いくつかの疑問と希望を確かめ、叶えるために hello_world モジュールを修正します。長くなったので[[別ページ>HelloYou]]に移ります。

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS