Lions' 本読み (55)

8 章のあたりをもごもごしてるんですが、sleep 手続きあたりの諸々について色々確認してみることに。例えば sched() の runin で sleep してるあたりを起点に確認してみるとして該当位置の記述が以下。

1953 sloop:
1954    runin++;
1955    sleep(&runin, PSWP);

ちなみに PSWP の定義は以下。

0154 #define PSWP    -100

ちょっと限定的ですがここ起点にて。sleep の定義の一部を以下に。

2066 sleep(chan, pri)
2067 {
2068    register *rp, s;
2069
2070    s = PS->integ;
2071    rp = u.u_procp;
2072    if(pri >= 0) {

PS の定義は以下なんですが、

0164 #define PS 0177776

これ、プロセッサレジスタの値がここにマップされている、と勝手に類推。整数型としてアクセスするので以下な構造体のメンバとして云々、というのもなかなか凄い。

0175 struct { int integ; };

で、カレントプロセスな proc 構造体オブジェクトを取得しておいて以下に制御が移ります。

2087    } else {
2088            spl6();
2089            rp->p_wchan = chan;
2090            rp->p_stat = SSLEEP;
2091            rp->p_pri = pri;
2092            spl0();
2093            swtch();
2094    }
2095    PS->integ = s;
2096    return;

この場合、引数 pri の値は -100 なので else なブロック (signal 無視) になります。このケイスであれば proc#0 が runin で PSWP な優先度で SSLEEP な状態になる、と。
その後 swtch 手続きが呼び出されて今動いていた proc#0 以外の p_stat が SRUN で p_flag に SLOAD が立ってる proc 配列の要素が選択されて動きはじめるのか。
signal を無視しない場合の sleep も、と思ったんですが

2073            if(issig())
2074                    goto psig;

な jmp 先の以下の記述が

2105 psig:
2106    aretu(u.u_qsav);

となってて trap1 手続きが云々などというコメントがあるのでこのあたりはトラップな次の章以降に取っておいた方が良さげ。

wakeup とか setrun なあたり

wakeup 手続きは先頭から順に proc 配列を手繰って引数の chan と p_wxhan 属性の値が同値な要素について setrun 手続きを呼び出しています (手続き引用略)。
で、setrun 手続きでは引数で渡された proc なオブジェクトについて

  • w_chan 属性を 0 で初期化
  • p_stat 属性を SRUN に設定
  • p_pri 属性が curpri より小さければ (カレントプロセスより優先度が高ければ) runrun 増分
    • 0755 な trap 以降の処理で swtch が呼び出されます
  • runout が 0 以外で p_flag の SLOAD が立ってない場合、runout が 0 で初期化されて wakeup(&runout) が呼び出されます

ええと、runout な状態が_スワップアウトされたプロセスがどれも実行可能状態にないため、何もすることがない (Lions' Commentary on UNIX より引用)_とのことなので、この場合は引数なプロセスが実行可能状態になるように思えるんですが、単純にプロセスの優先度が高いだけだと swtch で選択されないんですよね。swtch では優先度を云々する前に p_stat の状態と p_flag の状態でふるいにかけれられているので。
とりあえずの所は一旦 SRUN ではなくなってかつスワップアウトされたプロセスは生き返るのが大変なのだろうな、という理解で良いのかな。

expand も確認

この手続き、どこから呼び出されてるんだろ、と思い xref 確認してみることに。

  • main 手続き
  • exec 手続き
  • sbreak 手続き
  • grow 手続き
  • xalloc 手続き

なかなか大事そうなナニ達から呼び出されておられる模様で。
それは良いとして領域拡張な部分にフォーカスを当てて確認。

2281    savu(u.u_rsav);
2282    a2 = malloc(coremap, newsize);
2283    if(a2 == NULL) {
2284            savu(u.u_ssav);
2285            xswap(p, 1, n);
2286            p->p_flag =| SSWAP;
2287            swtch();
2288            /* リターンしない */
2289    }

これ、スワップアウトして行ってらっしゃい、なパターンですね。2284 で u.u_ssav に savu しなきゃいけない根拠としてはスワップアウトするケイスでは swtch 呼び出してその中で u.u_rsav に savu している (2189) ので、という事らしい。
これまで確認した中ではスワップインする場合は u.u_ssav から云々していたはず。
で、次は領域が正常に確保できたケイス。

2290    p->p_addr = a2;
2291    for(i=0; i<n; i++)
2292            copyseg(a1+i, a2++);
2293    mfree(coremap, n, a1);
2294    retu(p->p_addr);
2295    sureg();
2296 }

これ、基本的にはデータセグメントに特化した領域拡張なのか。てゆーか領域拡張があり得るのはデータセグメントだけなのか。
とりあえず最後で retu で確保した新たな領域をデータセグメント扱い (u が指してる状態) にして sureg 手続きでユーザ空間の APR 向けの属性を再設定している、ということなのか。
それ以外の属性については 2292 な copyseg で何とかなってる、というのが若干謎ですが、これは念の為、ってことなのかどうなのか。

9 章以降は

相当流して読んでいるので少しきちんと確認を入れたいと思っています。