NetBSD Advent Calendar 2023 11日目の記事です。今日はNetBSDカーネルモジュールのサンプルからカーネルの機能を見てみようと思います。
カーネルモジュールのサンプル
NetBSDカーネルモジュールのサンプルは、 /usr/src/sys/modules/examples
に置かれています。いくつかサンプルがありますが、今回は executor
を見てみます。
executorカーネルモジュール
まずはソースコードを見てみます。カーネルモジュールなので、ロード時の処理から追いかけて行くのが良さそうです。
90 static int
91 executor_modcmd(modcmd_t cmd, void *arg)
92 {
93 switch(cmd) {
94 case MODULE_CMD_INIT:
95 printf("executor module inserted\n");
96 callout_init(&sc, 0);
97 callout_reset(&sc, mstohz(1000), callout_example, NULL);
98 break;
99 case MODULE_CMD_FINI:
100 printf("executor module unloaded\n");
101 callout_stop(&sc);
102 callout_destroy(&sc);
103 break;
104 default:
105 return ENOTTY;
106 }
107 return 0;
108 }
カーネルモジュールがロードされると、94行目の MODULE_CMD_INIT
の処理に入ってきます。 callout_init()
で(おそらく)"callout"という機能の初期化を行っているようです。
callout_init(9)を見ると、"The callout facility provides a mechanism to execute a function at a given time."と説明されており、calloutという仕組み(facility)で指定した時間に関数を実行する機能が提供されているようです。
次に callout_reset()
を見てみます。以下のような形で関数が呼ばれており、指定した時間(ミリ秒をhzに変換している)の経過後に callout_example()
を呼び出させるような指定になっています。この使い方を見ると、JavaScriptの setTimeout()
に似ているように思えます。
callout_reset(&sc, mstohz(1000), callout_example, NULL);
さらにコードを読み勧めて callout_example()
を見てみます。 printf()
でメッセージを表示して callout_schedule()
を呼んでいます。callout(9)の説明を見ると、"callout_schedule() is slightly more efficient than using callout_reset()."とあるので、 callout_exeample()
の呼び出しを再度スケジューリングしているようです。
75 static void 76 callout_example(void *arg) { 77 RUN_ONCE(&ctl, runonce_example); 78 executor_count++;
79 printf("Callout %d\n", executor_count);
80 callout_schedule(&sc, mstohz(1000));
81 }
実際に挙動を確認してみる
ソースコードから動作内容がなんとなく見えてきたので実際に動かして確認してみます。まずはカーネルモジュールのビルドです(単に make
を走らせるだけです)。
# make
# compile executor/executor.o
/usr/src/tooldir.NetBSD-9.3-amd64/bin/x86_64--netbsd-gcc -O2 -g -std=gnu99 -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wno-sign-compare -Wsystem-headers -Wno-traditional -Wa,--fatal-warnings -Wreturn-type -Wswitch -Wshadow -Wcast-qual -Wwrite-strings -Wextra -Wno-unused-parameter -Wno-sign-compare -Wold-style-definition -Wsign-compare -Wformat=2 -Wno-format-zero-length -Werror -ffreestanding -fno-strict-aliasing -Wno-pointer-sign -mno-red-zone -mno-mmx -mno-sse -mno-avx -msoft-float -mcmodel=kernel -fno-omit-frame-pointer -I/usr/src/common/include --sysroot=/ -I/usr/src/common/include -nostdinc -I. -I/usr/src/sys/modules/examples/executor -isystem /usr/src/sys -isystem /usr/src/sys/arch -isystem /usr/src/sys/../common/include -D_KERNEL -D_LKM -D_MODULE -DSYSCTL_INCLUDE_DESCR -c executor.c
/usr/src/tooldir.NetBSD-9.3-amd64/bin/nbctfconvert -L VERSION executor.o
# link executor/executor.kmod
/usr/src/tooldir.NetBSD-9.3-amd64/bin/x86_64--netbsd-gcc --sysroot=/ -Wl,--warn-shared-textrel -Wl,-z,relro -nostdlib -r -Wl,-T,/usr/src/sys/../sys/modules/xldscripts/kmodule,-d -Wl,-Map=executor.kmod.map -o executor.kmod executor.o
/usr/src/tooldir.NetBSD-9.3-amd64/bin/nbctfmerge -t -L VERSION -o executor.kmod executor.o
#
# file executor.kmod
executor.kmod: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
カーネルモジュールをロードします。
# modload ./executor.kmod
ソースコードの字面から予想していた通り、 callout_example()
が一定時間毎に呼び出されています。
まとめ
カーネルモジュールのサンプルから、calloutの機能を概観してみました。カーネルが提供する機能を学ぶにはカーネルモジュールの形で試してみるのが良さそうです。