〆(.. )カリカリッ!! 遂に6回目! sched_lwp_fork()とsched_lwp_collect()です。これでsched_4bsd.cで実装している関数は終わります( ´∀`)bグッ!
まずはsched_lwp_fork()です。
460 void 461 sched_lwp_fork(struct lwp *l1, struct lwp *l2) 462 { 463 464 l2->l_estcpu = l1->l_estcpu; 465 }
やってることは単純ですね。l1のcpu利用度をl2にコピーするだけです。この関数はsys/kern/kern_lwp.cにあるsched_lwp_fork()から呼ばれます。
874 kdtrace_thread_ctor(NULL, l2); 875 lwp_initspecific(l2); 876 sched_lwp_fork(l1, l2); 877 lwp_update_creds(l2);
l2が新しく作っているスレッドになります。
sched_lwp_fork()に関しては関数にあるコメントで簡単に説明されています。
763 /* 764 * Create a new LWP within process 'p2', using LWP 'l1' as a template. 765 * The new LWP is created in state LSIDL and must be set running, 766 * suspended, or stopped by the caller. 767 */ 768 int 769 lwp_create(lwp_t *l1, proc_t *p2, vaddr_t uaddr, int flags, 770 void *stack, size_t stacksize, void (*func)(void *), void *arg, 771 lwp_t **rnewlwpp, int sclass)
プロセP2に新しいlwpを作るためにl1をテンプレートとして使うようですね。 初期状態ではlwpはアイドル状態(LSIDL)にするから呼び出し元で適切な状態に変更してねと書かれてますね。
それでは次にsched_lwp_collect()を見ましょう。
467 void 468 sched_lwp_collect(struct lwp *t) 469 { 470 lwp_t *l; 471 472 /* Absorb estcpu value of collected LWP. */ 473 l = curlwp; 474 lwp_lock(l); 475 l->l_estcpu += t->l_estcpu; 476 lwp_unlock(l); 477 }
やってることはこれも単純でカレントのlwpになるlのcpu利用度に引数できたtのcpu利用度を足すだけです。
この関数はsys/kern/kern_lwp.cのlwp_wait()から呼ばれます。
625 /* 626 * We're no longer waiting. Reset the "first waiter" 627 * pointer on the target, in case it was us. 628 */ 629 l->l_waitingfor = 0; 630 l2->l_waiter = 0; 631 p->p_nlwpwait--; 632 if (departed) 633 *departed = l2->l_lid; 634 sched_lwp_collect(l2); 635
この関数のforの無限ループの中でプロセスAに属する全てのlwpにループでアクセスしつつお互いに待ち合ったことによるデッドロックの検知やスレッドがデタッチされたなどの場合の処理をしてきて、何も待っている状態では無かった時にsched_lwp_collect()を呼ぶようになってます。l2はプロセスAに属するlwpです。
さて、これでsched_4bsd.cと関連する関数の一部は一応読み終わった訳ですがdecay_cpu()、resetpriority()はこのsched_4bsd.cを読む上で重要な位置にあると思います。スケジューラの概要はFreeBSDカーネルの設計と実装でも説明されている(まあ、実装方法はNetBSDとFreeBSDで違ってきますが)のであの本と合わせて読むのが良いですね。