KOZOS (22)
スデに酒が注入されてますが、明日くらいまでは集中してこっちな方向。
とは言え
継続なソレが分からなくなってたりして。とりあえず syscall_proc で掘削してみるか。
重複しますが syscall_proc 手続きを呼び出しているのは kozos.c で定義されている syscall_intr 手続き。
static void syscall_intr(void) { syscall_proc(current->syscall.type, current->syscall.param); }
逆に syscall_intr 手続きを呼び出しているのはどれかな。
find . -type f -print0 | xargs -0 -e grep -nH -e 'syscall_intr' ./kozos.c:232:static void syscall_intr(void) ./kozos.c:254: * syscall_intr(), softerr_intr() がハンドラに登録されているので, ./kozos.c:284: setintr(SOFTVEC_TYPE_SYSCALL, syscall_intr); /* システム・コール */ Grep finished (matches found) at Tue May 3 19:46:54
setintr 手続きは kozos.c の kz_start 手続きから呼ばれている模様。割り込みハンドラの登録、とコメントに記述されている。これはちょい前に掘ってる部分だな。
ちょっと整理する意味でも kozos.c の中の call graph てきなソレを確認した方が良さげ。
とゆーことで
以下。
thread_intr -+- syscall_intr -+-syscall_proc -+- getcurrent | | | +- call_functions | +- schedule -+- kz_sysdown | +- dispatch call_functions -+- thread_run -+- putcurrent | | | +- thread_init -+- thread_end | +- thread_exit kz_start -+- thread_run -+- putcurrent | | | +- thread_init -+- thread_end | +- setintr | +- dispatch kz_syscall syscall_intr softerr_intr
- kz_start は main.c の main 手続きから呼び出されてます
- kz_syscall 手続きは syscall.c の kz_run 手続きおよび kz_exit 手続きから呼び出されます
- thread_intr は setintr で softvec_setintr 手続きに渡されてます
- setintr は kz_start から呼び出されてます
- syscall_intr および softerr_intr は kz_start 手続きにおいて setintr 手続きに渡されます
微妙な部分を掘ってみる
ええと、thread_run 手続きの定義あたりの記述が以下。
/* システム・コールの処理(kz_run():スレッドの起動) */ static kz_thread_id_t thread_run(kz_func_t func, char *name, int stacksize, int argc, char *argv[])
thread_run が kickoff されてるのは、というと
- call_functions 手続き (kozos.c:197)
- kz_start 手続き (kozos.c:288)
なんですが、kz_run って何だっけorz
ええと、syscall.c で手続き定義されとるな。main.c で以下な記述になってます。
static int start_threads(int argc, char *argv[]) { kz_run(test08_1_main, "command", 0x100, 0, NULL); return 0; } int main(void) { INTR_DISABLE; /* 割込み無効にする */ puts("kozos boot succeed!\n"); /* OSの動作開始 */ kz_start(start_threads, "start", 0x100, 0, NULL); /* ここには戻ってこない */ return 0; }
ええと、kz_start に kz_run を呼び出す start_threads 手続きが渡されてます。kz_start はいっちゃん最初の kickoff なので
void kz_start(kz_func_t func, char *name, int stacksize, int argc, char *argv[])
な引数を
/* システム・コール発行不可なので直接関数を呼び出してスレッド作成する */ current = (kz_thread *)thread_run(func, name, stacksize, argc, argv);
で、うん、ってカンジで kickoff してるのか。kz_start 手続きは main 手続きからのみの呼び出しか。このあたりは最近のエントリでもごもごしてないのかなぁ。
してました
が、掘削具合が微妙。冗長かもしれませんがここから掘削。kz_start 手続きは適当にしか掘ってない模様。体力の続く限り (?) 頑張ってみます。とりあえず以下から。
void kz_start(kz_func_t func, char *name, int stacksize, int argc, char *argv[]) { /* * 以降で呼び出すスレッド関連のライブラリ関数の内部で current を * 見ている場合があるので,current を NULL に初期化しておく. */ current = NULL;
ヤッてるのはコメントの通りなんですが、current という変数は kozos.c の先頭部分あたりで定義 (宣言?) されてます。
static kz_thread *current; /* カレント・スレッド */
普通に考えると外部変数で static で (static はこの際関係ないですが) なので 0 初期化されるんでないの? というのはアマ。
このあたりは OS がヤッてくれるナニであって今はその OS 的なソレが bootstrap してる箇所ですので自分で NULL 初期化しないと駄目です。ちなみに NULL も自分で定義してないと駄目で defines.h にて以下。
#define NULL ((void *)0)
次の三行なナニは
readyque.head = readyque.tail = NULL; memset(threads, 0, sizeof(threads)); memset(handlers, 0, sizeof(handlers));
kozos.c の先頭部分で定義されてるソレの初期化ですね。
/* スレッドのレディー・キュー */ static struct { kz_thread *head; kz_thread *tail; } readyque; static kz_thread *current; /* カレント・スレッド */ static kz_thread threads[THREAD_NUM]; /* タスク・コントロール・ブロック */ static kz_handler_t handlers[SOFTVEC_TYPE_NUM]; /* 割込みハンドラ */
ちなみに THREAD_NUM は 6 で SOFTVEC_TYPE_NUM は intr.h で定義されてて以下。
#define SOFTVEC_TYPE_NUM 3
あるいは kz_thread とか kz_handler_t の定義は何かというと
kz_thread 型
_kz_thread 構造体で定義は kozos.c です。
kz_handler_t 型
typedef void (*kz_handler_t)(void);
ということは
- threads は要素は 6 で TCB な配列なのか
- handlers は関数ポインタな配列で要素の数は 3
ちなみにこの handlers という名前の配列の添字は intr.h で定義されてます。
#define SOFTVEC_TYPE_SOFTERR 0 #define SOFTVEC_TYPE_SYSCALL 1 #define SOFTVEC_TYPE_SERINTR 2
ちょい find-grep してみてやれ。
- ./kozos.c:285: setintr(SOFTVEC_TYPE_SOFTERR, softerr_intr); /* ダウン要因発生 */
- ./kozos.c:284: setintr(SOFTVEC_TYPE_SYSCALL, syscall_intr); /* システム・コール */
SOFTVEC_TYPE_SERINTR は未使用な模様。setintr 掘るべきなのかどうか。なんとゆーかソース見てるとコンテキストを見失いがちですよね。
って、変数初期化してる直後で setintr 呼びだしてるしorz
/* 割込みハンドラの登録 */ setintr(SOFTVEC_TYPE_SYSCALL, syscall_intr); /* システム・コール */ setintr(SOFTVEC_TYPE_SOFTERR, softerr_intr); /* ダウン要因発生 */
ええと setintr 手続きの定義が以下ですね。
/* 割込みハンドラの登録 */ static int setintr(softvec_type_t type, kz_handler_t handler) { static void thread_intr(softvec_type_t type, unsigned long sp); /* * 割込みを受け付けるために,ソフトウエア・割込みベクタに * OSの割込み処理の入口となる関数を登録する. */ softvec_setintr(type, thread_intr); handlers[type] = handler; /* OS側から呼び出す割込みハンドラを登録 */ return 0; }
softvec_setintr 手続きの定義は interrupt.c な模様です。
/* ソフトウエア・割込みベクタの設定 */ int softvec_setintr(softvec_type_t type, softvec_handler_t handler) { SOFTVECS[type] = handler; return 0; }
SOFTVECS は interrupt.h で以下な定義。
#define SOFTVECS ((softvec_handler_t *)SOFTVEC_ADDR)
SOFTVEC_ADDR って何、って思ったら直上で以下な定義。
/* 以下はリンカ・スクリプトで定義してあるシンボル */ extern char softvec; #define SOFTVEC_ADDR (&softvec)
えーと、リンカスクリプトが久々に出てきましたな。
MEMORY { ramall(rwx) : o = 0xffbf20, l = 0x004000 /* 16KB */ softvec(rw) : o = 0xffbf20, l = 0x000040 /* top of RAM */
わを、これって RAM の先頭の割り込みベクタテーブルじゃん。という事は softvec_setintr 手続きはこの領域に引数なナニを設定してるのか。
SOFTVECS[type] = handler;
0x40 って 64 bytes で 4 で割ったら 16 個ってことで良いのかな。
で、その直後で再度配列に云々してるんですがちょっとマテ。
softvec_setintr に渡されてるのは thread_intr 手続きだな。その後、handlers という配列に設定されてるのは引数で渡されるソレなので
/* 割込みハンドラの登録 */ setintr(SOFTVEC_TYPE_SYSCALL, syscall_intr); /* システム・コール */ setintr(SOFTVEC_TYPE_SOFTERR, softerr_intr); /* ダウン要因発生 */
の syscall_intr とか softerr_intr などになる。ここでポイント高いのは setintr 手続きの中で softvec_setintr 手続きに渡されている thread_intr 手続き
/* 割込み処理の入口関数 */ static void thread_intr(softvec_type_t type, unsigned long sp)
ということになるんですが、そろそろ限界。明日はここ起点でがっつり掘削予定。