NetBSD Advent Calendar 2023 24日目の記事です。今日はカーネルモジュールからNetBSDカーネル空間内のデータを操作してみます。
カーネル空間内のデータ
カーネルモジュールはその名前の通り、カーネル空間に組み込んで使用します。言い換えると、カーネル空間内のデータに自由にアクセスできるということになります。今日の記事では、作成したカーネルモジュールからカーネル空間内の既存のデータを操作してみます。
ホスト名書き換えカーネルモジュール
NetBSD(に限らずUNIX系OS)では、hostname(1)でホスト名を設定できます。カーネルの実装という観点からすると、カーネル内にホスト名の情報を保持する変数を用意する形で実装されています。具体的には/usr/src/sys/kern/kern_sysctl.cの以下の個所で宣言されている変数でホスト名が保持されています。
152 char hostname[MAXHOSTNAMELEN];
153 int hostnamelen;
MAXHOSTNAMELEN
は/usr/src/sys/sys/param.hで以下のように定義されています。
124 #define MAXHOSTNAMELEN 256 /* max hostname size */
char hostname[]
はカーネル空間内であればどこからでも参照・変更可能です。今回はデバイスファイルへのwrite(2)経由でホスト名を設定し、カーネルモジュール側で write()
されたデータを受け取り、新しいホスト名として設定するサンプルを作成してみます。
カーネルモジュールのサンプルコード
カーネル空間側(カーネルモジュール)のコードは以下になります。
使用するデバイス /dec/hostname_sample
への write(2)
は hostname_sample_write()
関数に紐づけられており、ユーザ空間から書き込まれたデータ(ホスト名文字列)をカーネル空間にコピーしたのち、変数 hostname
に設定することでホスト名を設定しています。
カーネルモジュールビルド用の Makefile
を作成します。この Makefile
とCソースファイルは /usr/src/sys/modules/example/hostname_sample
のようなディレクトリに配置します。
.include "../Makefile.inc"
#S?= /usr/src/sys
KMOD= hostname_sample
SRCS= hostname_sample.c
.include <bsd.kmodule.mk>
make
コマンドでビルドします。
# make
...
/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/hostname_sample -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 hostname_sample.c
/usr/src/tooldir.NetBSD-9.3-amd64/bin/nbctfconvert -L VERSION hostname_sample.o
# link hostname_sample/hostname_sample.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=hostname_sample.kmod.map -o hostname_sample.kmod hostname_sample.o
/usr/src/tooldir.NetBSD-9.3-amd64/bin/nbctfmerge -t -L VERSION -o hostname_sample.kmod hostname_sample.o
デバイスノードを作成します。データを書き込む想定であるため、 chmod 666
でデバイスファイルに書き込み権限を付与しておきます。これでカーネル側での準備は完了です。
# mknod /dev/hostname_sample c 210 0
# chmod 666 /dev/hostname_sample
ユーザ空間側のサンプルコード
ユーザ空間側のコードは以下になります。write(2)
でホスト名を設定し、read(2)
で現在のホスト名を取得しています。
コンパイルして実行してみます。無事にホスト名の設定と取得が行えていることが確認できます。
# gcc -Wall -g -o hostname_sample hostname_sample.c
#
# # 現在のホスト名を取得する。
# ./hostname_sample
mbsd93test
#
# # カーネルモジュール経由でホスト名を設定する。
# ./hostname_sample newname
newname
#
# # 設定されたホスト名を確認する。
# ./hostname_sample
newname
まとめ
カーネルモジュールからカーネル空間内の既存のカーネルデータを操作してみました。今回のサンプルはカーネル内のデータだけでなく、カーネルが提供する機能を試してみるケースにも応用できそうです。