システムコールの追加
概要
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がカーネルコンフィグファイルなので、必要とあればこれを編集すればよい*1。VMWare上でもカーネルのコンパイルはあっという間に終わる。
% 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