先の hello_world.c を修正して、課題だったパラメータを入力する、エラーコードを返す実装をしてみましょう。make 方法と Makefile は hello_world.c と変わらないので省略します。コンパイルが済んだと言う前提でこのページを書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | - | | | ! - | ! - | | ! - ! |
|
モジュールはパラメータを受け取ることができます。主な手段は module_param() マクロ, DEVICE_ATTR マクロ, debugfs, procfs です。ここでは module_param を試します。次の表にそれぞれの説明をまとめておきます。パラメータ引き渡し方法にファイル・システム名が書かれている場合は、そのファイル・システム上に作られたノードを通して設定します。設定単位はノードと設定対象の対応です。.config は linux-stable ディレクトリ(ソース・コードの基底ディレクトリ)にある .config に書かれた構築条件です。=Y になっている場合に利用可能です。
手段 | パラメータ引き渡し方法 | 設定単位 | .config | ドキュメント、特徴 |
module_param | insmod or modprobe command line, boot parameter, sysfs node | モジュール毎 | CONFIG_SYSFS | もっとも簡単です。変数名とパラメータ名を独立に指定できる module_param_named()、Call Back を実装できる module_param_cb() があります。 ノードは /sys/module/module_name/parameters/nameにできます。 |
DEVICE_ATTR | sysfs node | デバイス毎 | CONFIG_SYSFS | ドキュメントは Documentation/driver-model/device.txt, Documentation/filesystems/sysfs.txt に有ります。デバイス毎にパラメータを設定できます。デバイス毎ですので、モジュール・ロード時にデバイスが無い場合はパラメータを設定できません。実装か必要なコード量は多いです。 ノードは /sys/devices/subsystem_base/subsystem/* にできます。バスやクラス構造的に追いやすい /sys/bus/* 以下、/sys/class/* 以下、/sys/block/* 以下からもシンボリックリンクを通じてアクセスできます。 |
debugfs | debugfs node | 任意構造 | CONFIG_DEBUG_FS | ドキュメントは debugfs にあります。主に使う関数は debugfs_create_dir(), debugfs_create_file(), debugfs_remove(), debugfs_remove_recursive() です。整数を扱う単純なノードであればドキュメントあるいは include/linux/debugfs.h から使用例を探して使うのが良いでしょう。 普通の Linux の構成であれば debugfs は /sys/kernel/debug に mount されます。あるいは mount して使うことができます。 |
procfs | procfs node | 任意構造 | CONFIG_PROC_FS | procfs を中心にした使い方のドキュメントは有りません。include/linux/proc_fs.h より辿れる関数と Documentation/filesystems/seq_file.txt に使用例があるので参考にしてください。proc_create(), proc_create_data(), proc_mkdir(), remove_proc_entry() などの関数とマクロの使われ方を参考に使用して下さい。モジュールロード時にパラメータとして指定できません。module_init() で指定した関数内でノードを作成し、ロード直後にパラメータを設定することができます。 procfs は /proc に mount されます。 |
sysfs, procfs, debugfs は必ずあるのか?
sysfs, procfs, debugfs は .config の記述により有無を選択できます。このうち sysfs, procfs とも必ず存在すると仮定しても良いでしょう。sysfs または procfs を無くすと相当に不便です。system call に匹敵する機能を分担しています。
debufs はディストリビューションや評価環境によっては「無し」に設定されていることが有ります。debugfs で提供されるノードの機能は開発時は便利です。しかし、製品としてリリースできないようなセキュリティ・ホール、コンテンツ保護違反、NDA(機密保持契約)違反、動作が不安定になる機能が実装される傾向があります。debugfs (あるいはその一部ノード)を製品リリースに含めるかどうか精査した方が良いでしょう。
いずれの方法もノードができます。ノードは属性の設定次第で read/write が可能です。userland - kernel 双方向で更新して情報交換できます(次の注意参照)。
module_param を pchar 型パラメータで使う場合の注意
module_param を pchar 型(文字列)で扱う場合は、バッファ確保動作に注意して下さい。ユーザーがノードに書き込む場合は param_set_charp() 読み出す場合は param_get_charp() が動作します。 param_set_charp() の内部で動的にメモリ確保が行われます。巧妙な kmalloc_parameter() と maybe_kfree_parameter() の動作により初期値として指していたメモリ領域は誤って解放されないようになっています。一度でもユーザーがノードを経由して書き込んだ場合、動的にバッファは確保され、そのサイズは書き込んだ文字列を strlen() で測った長さ + 1 (NUL 終端された文字列を格納するのに必要十分な長さ) となります。十分なバッファが確保されていない可能性が有ります。Kernel 内から module_param 経由で文字列を返す場合ノードの属性を read only にして、初期文字列(バッファ)を使用し続ける様にするか、module_param_cb() を使用することを検討して下さい。
まず、パラメータ無しで insmod, rmmod を試します。ソース・コードに書かれたデフォルト値で動作しているのがわかります。
$ sudo insmod hello_world.ko [sudo] password for user_name:password $ sudo rmmod hello_world.ko $ dmesg
dmesg が出力した最後は次のようになります。your_name と return_value の値がそのまま表示されました。
[355028.315777] hello_world: module verification failed: signature and/or required key missing - tainting kernel [355028.319261] hello_world_init: Hello Taro san. return_value=0 [355036.306520] hello_world_exit: Goodbye Taro san.
パラメータを付けて起動しましょう。your_name=Hanako とします。
$ sudo insmod hello_world.ko your_name=Hanako $ sudo rmmod hello_world.ko $ dmesg
dmesg が出力した最後は次のようになります。your_name に指定した Hanako が表示されました。アプリケーション・プログラムと仕組みは違いますがパラメータを受け取る手段が有ります。
[355218.292794] hello_world_init: Hello Hanako san. return_value=0 [355236.027700] hello_world_exit: Goodbye Hanako san.
Kernel に組み込んだ後パラメータを変えてみます。パラメータは /sys/module/hello_world/parameters の下にノードとして存在します。return_value と your_name のつのパラメータを作ったのでノードは 2 つできています。パーミッションは module_param の引数で指定したとおりになっています。
$ sudo insmod hello_world.ko $ ls -la /sys/module/hello_world/parameters total 0 drwxr-xr-x 2 root root 0 8月 22 10:34 . drwxr-xr-x 6 root root 0 8月 22 10:34 .. -rw-r--r-- 1 root root 4096 8月 22 10:34 return_value -rw-r--r-- 1 root root 4096 8月 22 10:34 your_name
ノードのサイズが 4096 です。理由を探ってみましょう。sysfs_add_file_mode_ns() に size = PAGE_SIZE; と書いてあるところから由来します。binary data を直接扱わないノードであれば、4096 に固定されます。書き込みは最大で PAGE_SIZE 分だけ確保されます。内部では kernfs_fop_write() の実装により PAGE_SIZE + 1 だけ確保されたバッファが確保されます。
module_param を charp 型で使う場合の長さ制限
module_param() を charp 型で使う場合、書き込みの長さは param_set_charp() の実装により終端 NUL を含まない文字列の長さは 1024 バイトに制限されます。
グローバル変数 your_name 周辺の実装を見てバッファ・オーバーランが起きないのだろうか?と直感するでしょう。module_param 周りでかなり巧妙な実装がされています。Linux Kernel のソース・コードが追いにくい面も触れます。見ていくことにします。