カーネルモジュールとは
Linux カーネルにはモジュール機能が実装されており、デバイスドライバやファイルシステム等、独立したカーネル機能をカーネルモジュールとして作成し、必要に応じて動的にカーネル空間に組み込むまたは削除することが可能になっている。
カーネルモジュールは Linux カーネル空間に動的に組み込むことが可能なバイナリファイルとして作成され、拡張子は .ko となる。
カーネルソースファイル中に CONFIG_*=m とされた項目に対応するドライバはカーネルモジュールとして作成される。作成されたモジュールは通常 /lib/modules/kernel_version/kernel 配下にインストールされる。
$ ls -l /lib/modules/4.14.138-89.102.amzn1.x86_64/kernel/
total 40
drwxr-xr-x 3 root root 4096 Aug 26 18:05 arch
drwxr-xr-x 2 root root 4096 Aug 26 18:05 block
drwxr-xr-x 3 root root 4096 Aug 26 18:05 crypto
drwxr-xr-x 44 root root 4096 Aug 26 18:05 drivers
drwxr-xr-x 38 root root 4096 Aug 26 18:05 fs
drwxr-xr-x 7 root root 4096 Aug 26 18:05 lib
drwxr-xr-x 2 root root 4096 Aug 26 18:05 mm
drwxr-xr-x 30 root root 4096 Aug 26 18:05 net
drwxr-xr-x 3 root root 4096 Aug 26 18:05 security
drwxr-xr-x 3 root root 4096 Aug 26 18:05 virt
独自カーネルモジュールの作成
カーネルのソースツリーに含まれない、独自のカーネルモジュールを作成することも可能となる。
以下のようなコードを用意する。
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/errno.h>
static int sec = 5;
module_param(sec, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(sec, "Set the interval.");
static void mymod_timer(unsigned long data);
static DEFINE_TIMER(timer, mymod_timer, 0, 0);
static void mymod_timer(unsigned long data) {
printk(KERN_INFO "mymod: timer\n");
mod_timer(&timer, jiffies + sec * HZ);
}
static int mymod_init(void) {
printk(KERN_INFO "mymod: init\n");
if (sec <= 0) {
printk(KERN_INFO "Invalid interval sec=%d\n", sec);
return -EINVAL;
}
mod_timer(&timer, jiffies + sec * HZ);
return 0;
}
static void mymod_exit(void) {
del_timer(&timer);
printk(KERN_INFO "mymod: exit\n");
}
module_init(mymod_init);
module_exit(mymod_exit);
MODULE_AUTHOR("Yuki Yamaguchi");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My first module");
上記コードのポイントは以下の点となる。
- module_param でこのモジュールのパラメータ sec を設定する
- module_init() 及び module_exit() により、このモジュールの初期化処理 (ロードされた際の処理) として mymod_init() 及び終了処理 (アンロードされた際の処理) として mymod_exit() を定義する
- DEFINE_TIMER で mymod_timer() をこのモジュールのタイマーとして設定する
- mymod_init() では mod_timer() によりタイマーを登録しており、sec 秒後にタイマー関数が実行される
- sec 秒後に実行されたタイマー関数 mymod_timer() は再び sec 秒後にタイマーを設定するため、sec 秒ごとにタイマーが呼ばれる形となる
あとは以下のような Makefile を作って置く必要があります。
obj-m := mymod.o
あとは make するだけなんですが、ここで /lib/modules/`uname -r`/build のシンボリックリンクが生きてるか確認する必要がある。これが無いと実際に make した際に No such file or directory と言われて終了する。
/lib/modules/`uname -r`/build は対応したカーネルバージョンの kernel-header を指すことが期待されるため、もしシンボリックリンクが切れてるようなら以下のように対応したバージョンの kernel-header がインストールされているかを確認し、必要に応じてインストールする。
$ rpm -qa kernel* | grep `uname -r`
kernel-tools-4.14.154-99.181.amzn1.x86_64
kernel-4.14.154-99.181.amzn1.x86_64
kernel-headers-4.14.154-99.181.amzn1.x86_64
kernel-devel-4.14.154-99.181.amzn1.x86_64
kernel-tools-devel-4.14.154-99.181.amzn1.x86_64
あとは実際に make する。
$ make -C /lib/modules/`uname -r`/build M=`pwd`
make: Entering directory `/usr/src/kernels/4.14.154-99.181.amzn1.x86_64'
AR /home/ec2-user/mymod/built-in.o
CC [M] /home/ec2-user/mymod/mymod.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/ec2-user/mymod/mymod.mod.o
LD [M] /home/ec2-user/mymod/mymod.ko
make: Leaving directory `/usr/src/kernels/4.14.154-99.181.amzn1.x86_64'
実際に modinfo すると情報が取得できる。
$ modinfo mymod.ko
filename: /home/ec2-user/mymod/mymod.ko
description: My first module
license: GPL
author: Yuki Yamaguchi
srcversion: BC427786F9AA9F933ADC0A7
depends:
retpoline: Y
name: mymod
vermagic: 4.14.154-99.181.amzn1.x86_64 SMP mod_unload modversions
parm: sec:Set the interval. (int)
自作カーネルモジュールの組み込み
実際に作成したモジュールを組み込んでみる。
$ sudo insmod mymod.ko
$ dmesg | tail
[ 315.071059] mymod: loading out-of-tree module taints kernel.
[ 315.073950] mymod: init
[ 320.216068] mymod: timer
[ 325.336002] mymod: timer
[ 330.455939] mymod: timer
[ 335.575876] mymod: timer
[ 340.695812] mymod: timer
[ 345.815741] mymod: timer
[ 350.935680] mymod: timer
[ 356.055616] mymod: timer
上記より実際にモジュールがロードされ、タイマーが 5 秒置きに実行されていることがわかる。
なお、sec パラメータは以下のように確認が可能となる。
$ cat /sys/module/mymod/parameters/sec
5
モジュールパラメータは insmod 時に指定が可能。
$ sudo insmod mymod.ko sec=10
$ dmesg | tail
[ 470.202486] mymod: init
[ 480.214047] mymod: timer
[ 490.453917] mymod: timer
[ 500.693786] mymod: timer
[ 510.933656] mymod: timer
[ 521.173531] mymod: timer
[ 531.413402] mymod: timer
[ 541.653271] mymod: timer
[ 551.893143] mymod: timer
[ 562.133016] mymod: timer
今度は 10 秒置きに実行されていることが確認できる。
なお、モジュールの削除は rmmod で可能。
$ sudo rmmod mymod.ko
$ dmesg | tail
[ 510.933656] mymod: timer
[ 521.173531] mymod: timer
[ 531.413402] mymod: timer
[ 541.653271] mymod: timer
[ 551.893143] mymod: timer
[ 562.133016] mymod: timer
[ 572.372883] mymod: timer
[ 582.612756] mymod: timer
[ 592.852625] mymod: timer
[ 600.856307] mymod: exit