初めてのmodule

kernel-2.6で初めてkernel-moduleを作ってみようとしたが、先人の情報を検索してやってみたところ、いまいちうまくいかなかったので整理した。

コード

コードは簡単に、なにもしないコードにしてみる。

lkm.c:

#include <linux/module.h>/**必須*/
#include <linux/kernel.h>/**printk*/

int init_module(void)/**モジュール初期化時に呼ばれる*/
{
        printk(KERN_INFO "init\n");
        return 0;

}
void cleanup_module(void)/**モジュール削除時に呼ばれる*/
{
        printk(KERN_INFO "cleanup\n");
}

Makefile:

obj-m += hello-1.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

初めてのbuild

これで、makeすると、

$ make
make[1]: Entering directory `/lib/modules/2.6.12-1.1381_FC3smp/build'
  CC [M]  /home/foo/work/lkm/lkm.o
  Building modules, stage 2.
  MODPOST
  CC      /home/foo/work/lkm/lkm.mod.o
  LD [M]  /home/foo/work/lkm/lkm.ko
make[1]: Leaving directory `/lib/modules/2.6.12-1.1381_FC3smp/build'
$

こんな感じでkernel objectが作成される。/sbin/modinfoで情報をみてみると

$ /sbin/modinfo lkm.ko
filename:       lkm.ko
vermagic:       2.6.12-1.1381_FC3smp SMP 686 REGPARM 4KSTACKS gcc-3.4
depends:
srcversion:     0A1A69F99683F953DED1CB8
$

初めてのロード

出来ちゃったkoはinsmodでシステムに組みこむ。

$ sudo /sbin/insmod lkm.ko
Password: xxxxxxx
$ 

なにもでなくてがっかりだが、dmesgをみてみると読みこまれた足跡がある。

$ dmesg | tail -1
init
$ 

外すときにはrmmodで。

エントリポイントの名前を変えたい場合

kernel-2.4以降ではエントリポイントの名前を変更することが出来る。それは、module_init()とmodule_exit()というふたつのマクロで行なう。これらのマクロはlinux/init.hで定義されているのでこれもincludeすることになる。なんかごまかされているような気がするが気にしない。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init lkm_init(void)
{
        printk("init\n");
        return 0;

}
static void __exit lkm_exit(void)
{
        printk(KERN_INFO "cleanup\n");
}
module_init(lkm_init);
module_exit(lkm_exit);

ライセンスとか

kernel2.4からはライセンスとか書けるようになっている。これがないとinsmodでしかられてしまうかもしれない。定義はlinux/module.hでされている。

マクロ 意味
MODULE_LICENSE("GPL") ライセンスを文字列で指定。"GPL"/"GPL v2"など。指定できるのはlinux/module.hを参照のこと。
MODULE_AUTHOR("name") 作者を指定。"Full name "のように指定する。
MODULE_DESCRIPTION("description") モジュールに対する一行コメント。
MODULE_SUPPORTED_DEVICE("device") モジュールがサポートするデバイスを記述する。システムが最適な構成を作るために自動で読みこむしくみが出来たときのヒントらしい。今はそんな機能が無いので飾り。

モジュールパラメタを指定してみる

モジュールを読みこむときにパラメタを指定したいことがあるかもしれない。insmod lkm.ko hogehoge=5とか出来ると便利だ。そして出来る。
MODULE_PARAM(var, "spec")とすると、varというパラメタを"spec"という仕様で定義できる。specは以下のような感じで指定する。

spec := [min[-max]]type
min: ARRAYの最小値
max: ARRAYの最大値

上からも解るように配列も指定できちゃうという。typeは以下のようにして指定する。簡単だネ。

type 意味
b byte 8bit
h short 16bit
i 整数(int)
l long
s 文字列(char*で、insmod時にアロケートされる)

実体はstatic変数として定義するので、デフォルト値を与えることも出来る。
ではさっそくやってみよう。testという整数の4要素配列パラメタを定義して、最低3個を設定しなければいけないとする。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("xxxxxxxxxxxxxxxxxxxxxxxx");
MODULE_DESCRIPTION("null module");
MODULE_SUPPORTED_DEVICE("none");
static int test[4];
MODULE_PARM(test, "3-4i");
static int __init lkm_init(void)
{
        int i;
        for (i = 0; i < 4; i++) {
                printk("init %d\n", test[i]);
        }
        return 0;

}
static void __exit lkm_exit(void)
{
        printk(KERN_INFO "cleanup\n");
}
module_init(lkm_init);
module_exit(lkm_exit);

これをビルドしてmodinfoやinsmodしてみる。

$ modinf lkm.ko
filename:       lkm.ko
license:        Dual BSD/GPL
author:         XXXXXXXXXXXXXXXXXXXX
description:    null module
parmtype:       test:3-4i
vermagic:       2.6.12-1.1381_FC3smp SMP 686 REGPARM 4KSTACKS gcc-3.4
depends:
srcversion:     B26A314FF9EE62CDF7BA558
$ sudo /sbin/insmod lkm.ko test=1
$ dmesg|tail
... 
test: needs at least 3 arguments
lkm: `1' invalid for parameter `test'
init 1
init 0
init 0
init 0

うお、なんだこりゃ。エラーメッセージがでるものの、insmodされちゃった。どうやら自発的にエラーチェックしないといけないらしい。が、すでに設定されちゃっているのでしょうがない。これは少ないからまだしも、この状態で4個以上パラメタをわたすとどうなるんだか考えたくない。が、ここはキャシャーンがやらねば誰がやるということで、人柱。

$ sudo /sbin/insmod lkm.ko test=6,7,8,9,1,2,3,4,5
$ dmeg | tail
...
test: can only take 4 arguments
lkm: `6' invalid for parameter `test'
init 6
init 7
init 8
init 9

よごれていないかは解らないが、大きい部分については、だいじょうぶ、なのかな。