放送当日のG2Mの調子が悪くて前回の復習が中心になりますがご了承下さい。
sleepについては、問題ないですがsleep(2)はないので(システムコールではない)の区別するなら「sleepk」と言ったほうが良い
疑問
メモリマップドIOで良いか?
→良い。
PDP-11だと、RKADDRがメモリマップドIOの領域になり、CPUとDISK Controllerの両方から見える。
RDADDRを構造体deviceとしてアクセスすることにより、IOの指示を行う。
下図を参照して下さい。
○参考
最近の組み込みCPUはだいたいMemory mapped I/Oが多い
ARM、モトローラ、System360、PDP-11などもMemory mapped I/O
また、ポートマップドI/Oという方式もあり、これは、専用の命令(例:INTELだとIN命令、OUT命令)と別のアドレス空間によりIOを実現している。
iodone
→bufをロックしているので、ここでアンロックする。
bp->b_dev
major(0-255)、minor(0-255)でそれぞれ8ビットづづの値を保持する。
majorで装置の種類を判別し、minorで何番目の装置かを示す。
●疑問点
rkintrは、依頼した読み書きが終わった段階で割り込みとして呼ばれるという認識です。
→OK
しかしrkintrの最後にrkstartを実行しています。
rkstartは実際にDISKに対してコマンドを送信している処理だと思うのですが、ここで実行している理由は?
→rkstartは1度に1つの処理しか出来ない、遅延書き込みが発生したり、読み込みが続けて発生した場合を考えて次の処理用にrkstartを実行している。
キュー上にIOのリクエストが溜まっているので掃けるまで続く、なくなったらrkstartが失敗して元のルートに戻るはず。
図は、ファイルシステムの場合だと名前からi-Nodeを取得する処理が抜けている。
そうでなければ問題なし。
●前々回のCalloutからの宿題
357 alarm()
/* */
358 {
359 register struct proc *p;
360 register c;
361 register struct a {
362 int deltat;
363 } *uap;
364
365 uap = (struct a *)u.u_ap;
366 p = u.u_procp;
367 c = p->p_clktim;
368 p->p_clktim = uap->deltat;
369 u.u_r.r_val1 = c;
370 }
この辺のp_clktimを追っかけてみます。
clockに突き当たります。
http://tamacom.com/tour/kernel/unix/S/84.html#L122
109行目で1秒に1回実施(HZは1秒のはず)
117行目でプロセス構造体を全部舐め回して、118行目でステータス(p_stat)をチェックして生きていてなおかつゾンビ(SZOMB)ではないものを処理します。
119行目でpp->p_timeが127でなければデクリメントしています。pp->p_timeは(resident time for scheduling)と定義されているのでスケジューラから当初与えられた時間を
デクリメントしているのではないかと思います。この時間を使い切ったら強制的にカーネルモードに戻って再スケジュールする。
121から123行目がシグナルの発生場所です、pp->p_clktimが0になったらシグナルを発生させています。
てっきり、ここでSIGALRMを発生させていると思ったのですが、「SIGCLK」でした。
109 if(++lbolt >= HZ) {
110 if (BASEPRI(ps))
111 return;
112 lbolt -= HZ;
113 ++time;
114 spl1();
115 runrun++;
116 wakeup((caddr_t)&lbolt);
117 for(pp = &proc[0]; pp < &proc[NPROC]; pp++)
118 if (pp->p_stat && pp->p_stat119 if(pp->p_time != 127)
120 pp->p_time++;
121 if(pp->p_clktim)
122 if(--pp->p_clktim == 0)
123 psignal(pp, SIGCLK);
124 a = (pp->p_cpu & 0377)*SCHMAG + pp->p_nice - NZERO;
125 if(a < 0)
126 a = 0;
127 if(a > 255)
128 a = 255;
129 pp->p_cpu = a;
130 if(pp->p_pri >= PUSER)
131 setpri(pp);
132 }
133 if(runin!=0) {
134 runin = 0;
135 wakeup((caddr_t)&runin);
136 }
137 }
psignalを、もう少し追っかけてみます。
●psignal
http://tamacom.com/tour/kernel/unix/S/95.html#L55
63行目プロセスのp_sigにシグナルSIGCLKをセット(1引いて2倍?)
65行目で優先度セットして(PUSERより大きければPUSER)
66、67行目でプロセスが寝てて、優先度が0以上ならsetrunを実行
51 /*
52 * Send the specified signal to
53 * the specified process.
54 */
55 psignal(p, sig)
/* */
56 register struct proc *p;
57 register sig;
58 {
59
60 if((unsigned)sig >= NSIG)
61 return;
62 if(sig)
63 p->p_sig |= 1<<(sig-1);
64 if(p->p_pri > PUSER)
65 p->p_pri = PUSER;
66 if(p->p_stat == SSLEEP && p->p_pri > PZERO)
67 setrun(p);
68 }
続いて、setrun
●setrun
http://tamacom.com/tour/kernel/unix/S/96.html#L140
151行目でプロセスが実行待ちであれば直ちに起こす。
そうでなければ
156行目でプロセスのステータスをSRUN(実行中)にして
プロセスの優先度が現在の優先度より小さい(優先度が高い)ならば
プロセスをrunqueに入れて、runrun(カレントプロセスよりも優先度が高いプロセスの存在を示すフラグ)をインクリメントしてして、プロセスを起こします。
とここまで、書いてみて、「SIGALRM」がやっぱりない??
今回はここまでとしたいと思います。(残念)
136 /*
137 * Set the process running;
138 * arrange for it to be swapped in if necessary.
139 */
140 setrun(p)
/* */
141 register struct proc *p;
142 {
143 register caddr_t w;
144
145 if (p->p_stat==0 || p->p_stat==SZOMB)
146 panic("Running a dead proc");
147 /*
148 * The assignment to w is necessary because of
149 * race conditions. (Interrupt between test and use)
150 */
151 if (w = p->p_wchan) {
152 wakeup(w);
153 return;
154 }
155 p->p_stat = SRUN;
156 setrq(p);
157 if(p->p_pri < curpri)
158 runrun++;
159 if(runout != 0 && (p->p_flag&SLOAD) == 0) {
160 runout = 0;
161 wakeup((caddr_t)&runout);
162 }
163 }
●備考
プロセスの情報は以下に定義されています。
http://tamacom.com/tour/kernel/unix/S/71.html#L13
9 struct proc {
10 char p_stat;
11 char p_flag;
12 char p_pri; /* priority, negative is high */
13 char p_time; /* resident time for scheduling */
14 char p_cpu; /* cpu usage for scheduling */
15 char p_nice; /* nice for cpu usage */
16 short p_sig; /* signals pending to this process */
17 short p_uid; /* user id, used to direct tty signals */
18 short p_pgrp; /* name of process group leader */
19 short p_pid; /* unique process id */
20 short p_ppid; /* process id of parent */
21 short p_addr; /* address of swappable image */
22 short p_size; /* size of swappable image (clicks) */
23 caddr_t p_wchan; /* event process is awaiting */
24 struct text *p_textp; /* pointer to text structure */
25 struct proc *p_link; /* linked list of running processes */
26 int p_clktim; /* time to alarm clock signal */
27 };
プロセスの状態は以下に定義されています。
http://tamacom.com/tour/kernel/unix/S/71.html#L37
31 /* stat codes */
32 #define SSLEEP 1 /* awaiting an event */
33 #define SWAIT 2 /* (abandoned state) */
34 #define SRUN 3 /* running */
35 #define SIDL 4 /* intermediate state in process creation */
36 #define SZOMB 5 /* intermediate state in process termination */
37 #define SSTOP 6 /* process being traced */
●禁断の「setjmp」「longjmp」
sleep(3)のソースを見ていて話題になったのが、「setjmp」「longjmp」です。
説明によると、GOTOが同じ関数内で別の場所に飛ぶのに対して「setjmp」「longjmp」は任意の関数にいきなり戻れるようです。
通常はA→B→Cと読んだときにスタックに上から順にC、B、Aの情報が載っています。
CがリターンすればスタックのCの情報は無効になってBへ戻り、BがリターンすればばスタックのBの情報は無効になってAへ順番に戻ってきます。
しかし、「setjmp」「longjmp」を使えば、CからいきなりAへ戻れるそうです。
setjmpでAの状態を保存しておいて、Cでその保存しておいた情報を使ってAへ戻るようです。
例外処理や初期のスレッドはこれで実装していたとのことです。
以下の説明が詳しいので参考にして下さい。
Super Technique 講座
http://www.nurs.or.jp/~sug/soft/super/longjmp.htm
●メモリの再配置
PDP-11は16ビットCPUなので、アドレスは0~64KBのメモリ空間を持ちます。
カーネルもユーザプロセスも同じです。
それぞれが仮想空間の0~64KBで動作するようになっています。
ただそのままだと全てのプロセスが同じアドレスを使用するので衝突します。
そのため、実行時にメモリの再配置(リアロケーション)が必要になります。
PDP-11ですと、メモリが18ビットなので物理メモリは256KBになります。
メモリの再配置のイメージは以下参照
各プロセスは8KB単位の8つに分割します。1つをページと呼びます。つまり8ページに分割します。
ユーザモードとカーネルモードで変換表が2つあります。
この変換表により、仮想アドレス(VA)を物理アドレス(PA)に変換し各プロセスのメモリ再配置を行い衝突しないようにします。
PDP-11では、PSWの上位4ビットに現在、前回のモード(カーネル、ユーザ)の状態を保持しています。
それにより、変換表の切り替えを行います。
そのため、モードが切り替わると変換表自体が変わるので前のモードから値を渡したりすることが出来ません。
以前にシステムコールでシステムコールの番号(例:getpidは20)を取得するのに、前モードのアドレスから値を取得する関数fuiwordがありましたが、こういう理由で特殊な命令で値を渡していたわけです。
最近のLINUXなどは物理メモリ自体が大きいので、1GBを境界にして、カーネル空間とユーザ空間をそもそも分けています。
なので、ユーザプロセスからカーネル空間は見えているわけですが、メモリ保護機能により直接のアクセスを禁止しています。
→unix の実績を元にCPU側で保護機能を実装
ユーザプロセスからのアクセスは以前と同じようにシステムコールになります。
カーネル空間とユーザ空間は別なのでそこでのデータアクセスは実際はメモリコピーになりますが
伝統的なUNIXの用語をそのまま使用してcopyin, copyoutと呼ぶそうです。
0 件のコメント:
コメントを投稿