プロセス1実行編①(カーネルモード) void forkret(void) (Xv6を読む~OSコードリーディング~)
前回
jupiteroak.hatenablog.com
トップページ
jupiteroak.hatenablog.com
proc.c
https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L396
void forkret(void) { static int first = 1; // Still holding ptable.lock from scheduler. release(&ptable.lock); if (first) { // Some initialization functions must be run in the context // of a regular process (e.g., they call sleep), and thus cannot // be run from main(). first = 0; iinit(ROOTDEV); initlog(ROOTDEV); } // Return to "caller", actually trapret (see allocproc). }
新規に作成されたプロセスは、scheduler関数で実行対象として選ばれた後に、forkret関数を一番最初に実行します。
処理の内容
クリティカルセクション内でfirstフラグを設定する
static int first = 1;
ptableのロックを解放する前にfirstを1に設定します。
新規に作成されたプロセスは、scheduler関数の処理からptableのロックを取得したままの状態で、forkret関数を実行しています。
このクリティカルセクション内でfirstフラグを設定することで、新規に作成されたプロセスだけが、後に続く初期化処理を実行できるようにしています。
ptableのロックを解放する
// Still holding ptable.lock from scheduler. release(&ptable.lock);
scheduler関数の処理から取得したままになっているptableのロックを解放します。
プロセスの初期化処理を行う
if (first) { // Some initialization functions must be run in the context // of a regular process (e.g., they call sleep), and thus cannot // be run from main(). first = 0; iinit(ROOTDEV); initlog(ROOTDEV); }
前の処理で説明したように、ptableの操作を行うクリティカルセクション内でのみfirstフラグが1となるので、新規に作成されたプロセスだけが、この初期化処理を実行できます。
first = 0;
firstフラグを0に戻しておきます。
iinit(ROOTDEV);
iinit関数を呼び出して、iノードのキャッシュ領域を初期化しておきます。
initlog(ROOTDEV);
initlog関数を呼び出して、log構造体(ロギングに関わるデータ構造)を初期化しておきます。
forkret関数の実行後
forkret関数を実行完了すると、forkret関数の呼び出し元に戻る処理が始まります(スタックのトップに退避されている値をeipレジスタに復帰する処理が始まります)。allocproc関数の処理でみたように、スタックのトップにはtrapretのアドレスが退避されているので、fork関数を実行完了すると、trapretの処理に移ります。