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 章以降は
相当流して読んでいるので少しきちんと確認を入れたいと思っています。