スケジューラ

Plan9のスケジューリング方式は基本的にはUNIXと同じ優先度つきラウンドロビンである.リアルタイムプロセス用にEDFスケジューリングも可能だが,まずはEDFは無視して,読み進めようと思う.
優先度は22レベルあり,最上位の二つはEDFスケジューリング用に予約されている.UNIXと違って,優先度は数字が大きな方が優先度が高い.優先度関係はport/portdef.hで定義されている.

  582:         Npriq          = 20,         /* number of scheduler priority levels */
  583:         Nrq            = Npriq+2,    /* number of priority levels including real time */
  584:         PriRelease     = Npriq,      /* released edf processes */
  585:         PriEdf         = Npriq+1,    /* active edf processes */
  586:         PriExtra       = 0,          /* edf processes we don't care about */
  587:         PriNormal      = 10,         /* base priority for normal processes */
  588:         PriKproc       = 13,         /* base priority for kernel processes */
  589:         PriRoot        = 13,         /* base priority for root processes */

昨日書いたように,スケジューラの本体の関数はrunproc (port/proc.c)である.レディキューのrunqはSched構造体の配列であり,各優先度のエントリからProc構造体が(片方向)リンクリストで連結されている.runvecはrunqの各エントリにプロセスが存在するか示すビットマップである.

少し注目すべきところは,マルチプロセッサに対応しているので,プロセッサアフィニティも考慮してスケジューリングしている点だろうか.つまり,以前プロセッサAで実行されたプロセスは,次もプロセッサAで実行することで,キャッシュの恩恵を受けやすくなるように工夫している.なお,p->mpは,前回実行されたプロセッサ(struct Mach)を指している.また,p->wiredを設定することで,必ず特定のプロセッサで実行するように指定できる(procwired関数経由かな).

スケジューラの核は529行から始まるforループである.最初に(i == 0のとき)このループを実行するときは,優先度の高いプロセスから探索し,プロセッサアフィニティがあれば(p->mp == MACHP(m->machno)),そのプロセスを選択する.最初のループで実行可能なプロセスが見つからなかった場合は,wiredされていないプロセスも含め,選択の幅を広げて再探索する.

プロセスを発見したら,546行に飛び,レディキューからプロセスを取り出し,p->stateとp->mpを設定し,リターンする.

   40: Schedq  runq[Nrq];
   41: ulong   runvec;

  495: Proc*
  496: runproc(void)
  497: {
  498:         Schedq *rq;
  499:         Proc *p;

  501:         int i;

  516: loop:

  522:         spllo();
  523:         for(i = 0;; i++){

  529:                 for(rq = &runq[Nrq-1]; rq >= runq; rq--){
  530:                         for(p = rq->head; p; p = p->rnext){
  531:                                 if(p->mp == nil || p->mp == MACHP(m->machno)
  532:                                 || (!p->wired && i > 0))
  533:                                         goto found;
  534:                         }
  535:                 }
  536: 
  537:                 /* waste time or halt the CPU */
  538:                 idlehands();

  544:         }
  545: 
  546: found:
  547:         splhi();
  548:         p = dequeueproc(rq, p);
  549:         if(p == nil)
  550:                 goto loop;
  551: 
  552:         p->state = Scheding;
  553:         p->mp = MACHP(m->machno);

  562:         return p;
  563: }

今日はここまでにするが,キュー操作用の関数として,dequeueprocとqueueprocという関数が使われている.