システムコールの追加

概要

Plan9システムコールを追加してみる。この手の演習ではgetpidを作ることになっているので、それに倣う。ちなみにPlan9のgetpid(2)は/dev/pidを読んで表示するというライブラリ関数である。ということで、kgetpidを実装してみよう。

関係するファイルは次の4つ。最後のファイルにシステムコール本体を実装するが、どのファイルに追加するかは機能に依存する。

  • /sys/src/libc/9syscall/sys.h
  • /sys/include/libc.h
  • /sys/src/9/port/systab.h
  • /sys/src/9/port/sysproc.c

libcの変更

システムコールを列挙したファイル/sys/src/libc/9syscall/sys.hの最後に次の行を追加する。これはlibcだけではなく、カーネルにも関係する。「Plan9のシステムコール数」でも書いたけど、システムコール数は51個と少ない。

#define	KGETPID		52

続いてユーザコードから呼び出すための関数宣言として/sys/include/libc.hに次の行を追加する。

extern	int	kgetpid(void);

libcをコンパイルする。mk installとかしなくても、/386/lib/libc.aが更新される。

% cd /sys/src/libc
% mk

カーネルの変更

システムコール表を定義している/sys/src/9/port/systab.hは、先ほど編集したsys.hとmksystabスクリプトから生成される。システムコールを呼び出すスタブコードは自動生成されるので、「sys + システムコール」という名前の関数を実装すればよい。詳細は「システムコールの実装」を参照してちょうだい。

% cd /sys/src/9/port
% cp systab.h systab.h.orig
% rc mksystab > systab.h
% diff systab.h.orig systab.h
54a55
> Syscall syskgetpid;
107a109
> [KGETPID] syskgetpid,
160a163
> [KGETPID] "Kgetpid",

syskgetpidの中身はこんな感じ。

long
syskgetpid(ulong *arg)
{
	return up->pid;
}

カーネルコンパイル

(デフォルトカーネルである)fossilベースのカーネルの名前は9pcfになっている。pcfがカーネルコンフィグファイルなので、必要とあればこれを編集すればよい*1VMWare上でもカーネルコンパイルはあっという間に終わる。

% cd /sys/src/9/pc
% mk 'CONF=pcf'

あとは、9fat上にファイルをコピーして、リブートすればOK。念のため古いカーネルも残しておこう。

% 9fat:
% mv /n/9fat/9pcf /n/9fat/9pcf.ORIG
% cp 9pcf /n/9fat

で、リブート。

% fshalt -r

簡単なマイクロベンチマーク

新しいカーネルからのブートに成功したら、kgetpid(1)が使えるようになる。ここで簡単なベンチマークをしてみよう*2。1回あたりの実行時間は、getpid(2)は60マイクロ秒に対してkgetpid(1)は1マイクロ秒となった。まぁ、VMWare Fusion on MacBook上の値なので、参考程度の数値だが。

#include <u.h>
#include <libc.h>

#define N 10000

void
main(int argc, char **argv)
{
	int i, pid;
	vlong start, end;

	start = nsec();
	for (i = 0; i < N; i++)
		pid = getpid();
	end = nsec();
	print("getpid=%d: %g\n", pid, (double)(end - start) / (double)N);

	start = nsec();
	for (i = 0; i < N; i++)
		pid = kgetpid();
	end = nsec();
	print("kgetpid=%d: %g\n", pid, (double)(end - start) / (double)N);
	exits(nil);
}

ブートに失敗した場合

テキストモードではacmeは使えない。9fatをマウントしてedでplan9.iniを編集できるようにしておく。edはUNIXでも動く由緒正しきラインエディタ。

% 9fat:
% cd /n/9fat
% ed plan9.ini

例えば、元のカーネルに戻す場合の例を示す。「.」はカレント行を表示するコマンド。通常、カーネルのパスは1行目に書かれているので、この9pcfを9pcf.ORIGに置換する。置換は「$s」コマンド。「1,」は1行が対象であることを意味する。「w」で書込み、「q」で終了。

.
bootfile=sdC0!9fat!9pcf
1,$s/9pcf/9pcf.ORIG/g
.
bootfile=sdC0!9fat!9pcf.ORIG
w
q

まとめ

Plan9システムコールkgetpidを追加した。Linuxのようにカーネルと(g)libcが分離されていないので、変更作業が一貫しておりスムーズにシステムコールを追加できる。また、圧倒的にコードサイズが小さいので、コンパイルも速い。

*1:BSDカーネルコンパイルみたい。

*2:エポック時間からの経過時間を知るためにtime(2)とnsec(2)というライブラリ関数が存在する。前者は秒単位、後者はナノ秒単位である。