はじめに
前回に引き続き、怒田晟也さん著“自作OSで学ぶマイクロカーネルの設計と実装”のIDEAS.md
より、マイクロカーネルHinaOSにシステムコールを追加します。
実装概要
割り込みの発生回数を取得するシステムコールを実装します。
あたりをつける
kernel/syscall.c
がシステムコール定義場所になっています。このファイル内容を見る限り、sys_uptime
関数がカーネル内の処理に関連する統計情報を返す関数になっているので、この関数まわりを踏襲することで、目的を達成できそうです。
// 起動してからの経過時間をミリ秒単位で返す。
static int sys_uptime(void) {
return uptime_ticks / TICK_HZ;
}
実装
1. ユーザランド側の関数呼び出し定義
各システムコールは、32bitのシステムコール番号で識別され、指定のレジスタに格納されたシステムコール番号に応じてカーネルモード時に処理を行います。libs/common/types.h
にその定義場所があるので、定義します。
#define SYS_INTERRUPT_COUNT 18
次に、libs/user/syscall.c
に以下の関数を定義します。特に渡したい値はないので、0 ~ 4番目の引数は0にします。最後の引数はカーネルモード時に関数を実行する識別子になるので、先ほど定義した変数を入れます。
// interruptシステムコール: システムの割り込み回数の取得
int sys_interrupt_count(void) {
return arch_syscall(0, 0, 0, 0, 0, SYS_INTERRUPT_COUNT);
}
2. 割り込み回数カウントを定義
グローバル変数uptime_ticks
は、interrupt.h
に定義されているので、同様に割り込み回数カウントをinterrupt.h
に定義します。ちなみに、extern
修飾子は外部ファイルから参照可能な変数であることを表します。
extern unsigned uptime_ticks;
extern unsigned interrupt_count; // 割り込み回数の総数
上記に加え、interrupt.c
ファイル内でもinterrupt_count
を宣言&初期化しないと怒られます。これは、ヘッダファイル内のextern
で定義と初期化が同時にできないためです。
unsigned interrupt_count = 0;
3. 統計情報取得実装
割り込み・例外ハンドラのエントリポイントとなるkernel/riscv32/trap.c
のriscv32_handle_trap
関数の頭に先ほど定義したinterrupt_count
のインクリメント処理を加えます。
void riscv32_handle_trap(struct riscv32_trap_frame *frame) {
stack_check(); // スタックオーバーフローをチェック
interrupt_count++;
...
4. システムコール定義
libs/user/
にて、ユーザランド側のシステムコールのインタフェースとなるAPIを定義します。
// libs/user/syscall.h
int sys_interrupt_count(void);
// libs/user/syscall.c
static int sys_interrupt_count(void) {
return interrupt_count;
}
5. システムコールハンドラ追記
/kernel/syscall.c
のhandle_syscall
にて、システムコール番号のパターンマッチを行う箇所があるので、そこにケースを追記します。
// システムコールハンドラ
long handle_syscall(long a0, long a1, long a2, long a3, long a4, long n) {
long ret;
switch (n) {
// ...
case SYS_INTERRUPT_COUNT:
ret = sys_interrupt_count();
break;
// ...
}
return ret;
}
6. コマンドを定義する
シェルサーバに割り込み回数を取得するコマンドを定義します。interruptは文字数が長く、タイポする可能性があるので、intpt
というコマンドとして定義します。
static void do_interrupt_count(struct args *args) {
printf("%d times\n", sys_interrupt_count());
}
// ...
static struct command commands[] = {
// ...
{.name = "intpt", .run = do_interrupt_count, .help = "get interrupt count"},
// ...
};
7. 実行
shell> intpt
15279 times
shell> intpt
18689 times
shell> intpt
36608 times
一秒あたり2000回ほど割り込まれています。タイマ割り込みも含まれているので、このような数字になるのだと思います。