●前回の補足
・forkとexecのなぞ
forkしてexecというのは知っていましたが何故というのはずっともやもやでした。
forkしてからexecしないことはないので最初から目的とするプロセスを起動すれば良いのにと。
前回でパイプをやりましたがそれでやっと分かりました。
forkして分身を作ってからexecする間に標準入出力をパイプに切り替えるために、分かれているんですね。
・dup2について
dup2(fildes, fildes2)
fildesをfildes2にコピーします。
fildesは既にオープンしているファイルのFD(コピー元)です。
fildes2がfildesと等しくなく、既にオープンしているファイルのFDの場合、まずクローズされます。
なので
close(fildes2)
dup(fildes)
と同じです。
fd = open("file", O_RDWR);
dup2(fd, 1)
とすれば、標準出力がファイルに切り替わります。
・パイプの実体
メモリ上に構築されていると思っていたのですがソースを辿っていくと
http://www.tamacom.com/tour/kernel/unix/S/8.html#L72
70 int rootdev = makedev(0, 0);
71 int swapdev = makedev(0, 0);
72 int pipedev = makedev(0, 0);
makedev(0, 0)としていますので通常のファイルシステムと同じように見えます。
これは今後の課題ということで
●プロセスの停止処理
shell上で
$ sleep 10
と実行した場合には、10秒経ってからプロンプトが表示されます。
$ sleep 10 &
と実行した場合には、すぐにプロンプトが表示されます。
この違いは何でしょうか?
通常の場合、親プロセスはforkした後でwaitで子プロセスの終了を待ちます。
&を付けた場合はwaitをしないので即時にコマンドプロンプトへ戻ってきます。
プロセスの停止で参照すべきシステムコールは、exitとwaitになります。
●処理の概要
プロセスには親子関係があるため、プロセスが停止する場合には自分の子(図では孫プロセス)と親(図では親プロセス)に対して適切な処理をしないと資源が開放されずに残ったり、プロセスの行き場がなくなったりします。
概要としては
・自分の資源は自分で出来るところまで解放
・自分の子プロセスの親を1(init)にして引き継ぎ
・出来るところまでやったら、後は親プロセスに依頼
●exit
マニュアルのP222
exit - terminate process
exit(status)
Exit is the normal means of terminating a process. Exit closes all the process’s files and notifies the
parent process if it is executing a wait. The low-order 8 bits of status are available to the parent process.
This call can never return.
(要約)
Exitはプロセスを終了する通常の手段です。
もし、親プロセスがwaitを実行している場合、Exitは全てファイルをクローズして親に通知します。
ステータスの下位8ビットは親プロセスが利用できます。このコールはリターンしません。
ソースファイルを見てみます。
http://www.tamacom.com/tour/kernel/unix/S/98.html#L325
基本はプロセスを破棄するために各種資源を初期化して、ゾンビ状態になります。
まずは自分の資源の解放
331行目:u構造体をたどってプロセス構造体をpに代入
332行目:プラグを落として
333行目:クロックタイムを0に初期化
334~335行目:シグナルu_signalの配列を全て1で初期化(0じゃないの?)
338行目:FDを格納している配列:u_ofileを全てNULLで初期化
339行目:FDは全てクローズ
341行目:パイプをロックして
342行目:inodeのリファレンスカウントをデクリメント。inodeのリファレンスカウントが1ならinodeを開放してパイプをアンロック
347行目:xfreeでプロセスの共有テキストセグメントの使用を破棄します
348行目:acctでアカウンティングファイル?に書き込みをしている
349行目:mfreeでユーザ空間を開放
350行目:プロセスの状態をゾンビ(SZOMB)に設定
351行目:xp_xstatにパラメータとして受け取ったExit番号を格納(これがwaitに渡ります)
352行目:ユーザタイムを計算します
353行目:システムタイムを計算します
子プロセスへの処理です。
354~360行:自分の子プロセスを探して後処理をします。プロセス構造体は最初からなめます。
355行目:プロセスの親プロセス番号が自分のプロセス番号と等しい(つまり自分の子プロセス)
356行目:プロセス番号1(init)を起こします
357行目:子プロセスの親プロセスを1(init)に変更します
358、359行:子プロセスがSTOPしているときは実行(RUN)状態にします
ここまでやれることは全部やって、最後の処理は自分で出来ないので親を起こしてお願いします
361~366行:自分の親プロセスを捜して後処理を依頼します。プロセス構造体は最初からなめます
362行目:プロセスの番号が自分の親プロセス番号と等しい(つまり自分の親プロセス)
363行目:親プロセス(q)を起こします。
364行目:プロセスをスイッチします。
プロセススイッチするので、Exitはリターンしません。
318 /*
319 * Release resources.
320 * Save u. area for parent to look at.
321 * Enter zombie state.
322 * Wake up parent and init processes,
323 * and dispose of children.
324 */
325 exit(rv)
/* */
326 {
327 register int i;
328 register struct proc *p, *q;
329 register struct file *f;
330
331 p = u.u_procp;
332 p->p_flag &= ~(STRC|SULOCK);
333 p->p_clktim = 0;
334 for(i = 0; i < NSIG; i++)
335 u.u_signal[i] = 1;
336 for(i = 0; i < NOFILE; i++) {
337 f = u.u_ofile[i];
338 u.u_ofile[i] = NULL;
339 closef(f);
340 }
341 plock(u.u_cdir);
342 iput(u.u_cdir);
343 if (u.u_rdir) {
344 plock(u.u_rdir);
345 iput(u.u_rdir);
346 }
347 xfree();
348 acct();
349 mfree(coremap, p->p_size, p->p_addr);
350 p->p_stat = SZOMB;
351 ((struct xproc *)p)->xp_xstat = rv;
352 ((struct xproc *)p)->xp_utime = u.u_cutime + u.u_utime;
353 ((struct xproc *)p)->xp_stime = u.u_cstime + u.u_stime;
354 for(q = &proc[0]; q < &proc[NPROC]; q++)
355 if(q->p_ppid == p->p_pid) {
356 wakeup((caddr_t)&proc[1]);
357 q->p_ppid = 1;
358 if (q->p_stat==SSTOP)
359 setrun(q);
360 }
361 for(q = &proc[0]; q < &proc[NPROC]; q++)
362 if(p->p_ppid == q->p_pid) {
363 wakeup((caddr_t)q);
364 swtch();
365 /* no return */
366 }
367 swtch();
368 }
●wait
マニュアルのP263
wait – wait for process to terminate
wait(status)
int *status;
wait(0)
Wait causes its caller to delay until a signal is received or one of its child processes terminates. If any
child has died since the last wait, return is immediate; if there are no children, return is immediate with
the error bit set . The normal return yields the process ID of the terminated
child.
If (int)status is nonzero, the high byte of the word pointed to receives the low byte of the argument of
exit when the child terminated. The low byte receives the termination status of the process.
A special status(0177) is returned for a stopped process which has not terminated and can be restarted.
If the parent process terminates without waiting on its children, the initialization process (process ID =
1) inherits the children.
(要約)
Waitはシグナルを受信するかそのプロセスのいずれかが終了するまで、呼び出し元に遅延を発生させます。
もし最後のwait以降に子プロセスが死亡した場合は、ただちにリターンします。
もし子プロセスが存在しない場合はエラービットを設定してただちにリターンします。
もしstatusが0でない場合、WORDの上位バイトはexitが終了したときのexitの引数下位バイトを示します。
下位バイトプロセスの終了ステータスを受け取る。
特別なステータス(0177)は終了しておらず再スタートできる状態の停止しているプロセスに対して返されます。
もし、親プロセスが子プロセスを待たずに終了した場合、初期化プロセス(プロセスIDが1)が子供を継承します。
waitソースを見てみます。
exitで子プロセスが出来るところまで自分でやって親を起こしたので、これは親プロセス側の後処理になります。
385~411目:プロセス構造体は最初からなめます
386行目:プロセスの親プロセス番号が自分のプロセス番号に等しい(つまり自分の子プロセス)
388行目:子プロセスの状態がゾンビ状態だったら以下を実行
389行目:システムコールのリターン値1に子プロセスのプロセス番号を格納
390行目:システムコールのリターン値2にxp_xstat、exitのパラメータで指定した番号を格納
→exitの351行目でパラメータ:rvをxp_xstatに代入している。
391行目:ユーザタイムを足しこみます。
392行目:システムタイムを足しこみます。
393~398行:プロセス番号、親プロセス番号などを0に初期化していきます。
399行目:プロセスの状態をNULLに設定
→これで子プロセスの後始末が終わりました。
400行目:リターンします
402行目:子プロセスの状態がSTOPの場合(マニュアルの0177の説明参照)
403行目:プロセスのフラグがSWTED?ではない場合
404行目:フラグにSWTEDを立てて
405行目:システムコールのリターン値1に子プロセスのプロセス番号を格納
406行目:システムコールのリターン値2に0177を立てて
407行目:リターン
412行目:fが0以外になるのは、for文の子プロセスが見つかった場合、387行目でインクリメント
ここにくるのは、子プロセスは見つかったがゾンビでも停止でもない場合
413行目:一定時間スリープして
414行目:loop(384行目)へGOTOします。
416行目:ここにくるのは子プロセスが存在しない場合、ECHILDをエラーに設定して関数を抜けます。
370 /*
371 * Wait system call.
372 * Search for a terminated (zombie) child,
373 * finally lay it to rest, and collect its status.
374 * Look also for stopped (traced) children,
375 * and pass back status from them.
376 */
377 wait()
/* */
378 {
379 register f;
380 register struct proc *p;
381
382 f = 0;
383
384 loop:
385 for(p = &proc[0]; p < &proc[NPROC]; p++)
386 if(p->p_ppid == u.u_procp->p_pid) {
387 f++;
388 if(p->p_stat == SZOMB) {
389 u.u_r.r_val1 = p->p_pid;
390 u.u_r.r_val2 = ((struct xproc *)p)->xp_xstat;
391 u.u_cutime += ((struct xproc *)p)->xp_utime;
392 u.u_cstime += ((struct xproc *)p)->xp_stime;
393 p->p_pid = 0;
394 p->p_ppid = 0;
395 p->p_pgrp = 0;
396 p->p_sig = 0;
397 p->p_flag = 0;
398 p->p_wchan = 0;
399 p->p_stat = NULL;
400 return;
401 }
402 if(p->p_stat == SSTOP) {
403 if((p->p_flag&SWTED) == 0) {
404 p->p_flag |= SWTED;
405 u.u_r.r_val1 = p->p_pid;
406 u.u_r.r_val2 = (fsig(p) << 8) | 0177;
407 return;
408 }
409 continue;
410 }
411 }
412 if(f) {
413 sleep((caddr_t)u.u_procp, PWAIT);
414 goto loop;
415 }
416 u.u_error = ECHILD;
417 }
疑問
&はwaitしないとのことでしたが、子プロセスが終了した後の後始末はどうなるんでしょうか?
マニュアルにあったように、initがやってくれるのでしょうか?
0 件のコメント:
コメントを投稿