Lions’ Commentary on UNIX 読書会メモ#7
トレースの流れについて
トレースの流れが話題に登りました。
この絵は以前のエントリで描いたものの再掲です。
child processがptraceを呼んでSTRCフラグをセットするタイミング
いつchild processはSTRCフラグをセットするんだ?というのが話題に上がりました。
ptraceシステムコールを使っているcdbのソースを覗いてみると、runcom( )のcase'r'のところに以下の記述があります。
http://www.bsdlover.cn/study/UnixTree/V6/usr/source/s1/cdb1.c.html
if ((pid = fork())==0) { ptrace(SETTRC, 0, 0, 0); // SETTRC = 0 signal(SIGINT, 0); signal(SIGINS, 0); doexec(); printf("Can't execute %s\n", symfil); exit(0); }
libcのfork( )の返り値は、親プロセス(fork実行プロセス)の場合は生成された子プロセスのpid(0以外)、子プロセス(forkで生成されたプロセス)の場合は0です。
なので、子プロセスのみがif文がtrueとなり、ptrace(SETTRC, 0, 0, 0)(=STRCフラグを立てる)が実行されます。
子プロセスはfork -> ptrace -> execという流れになっています。
fork( )からの返り値についてはsystem callのマニュアルに書いてありました。引用しておきます。
From C, the child process receives a 0 return, and the parent receives a non-zero number which is the process
ID of the child; a return of −1 indicates inability to create a new process.
libcのforkソースコードも引用しておきます。1:の部分で返り値であるr0を0クリアしているのがわかります。
http://www.bsdlover.cn/study/UnixTree/V6/usr/source/s4/fork.s.html
/ C library -- fork / pid = fork(); / / pid == 0 in child process; pid == -1 means error return / in child, parents id is in par_uid if needed .globl _fork, cerror, _par_uid _fork: mov r5,-(sp) mov sp,r5 sys fork br 1f bec 2f jmp cerror 1: mov r0,_par_uid clr r0 2: mov (sp)+,r5 rts pc .bss _par_uid: .=.+2
親プロセスが子プロセスに介入するタイミングについて(親プロセスがptrace( )を実行するタイミングについて)
親プロセスがwait( )を抜けた後にptrace( )を実行して子プロセスに介入しているはずという話をしたのですが、それが本当かどうかというのが話題に上がりました。
cdbのソースを見るとbpwait( )(wait( )を呼ぶ関数)の実行後にptrace( )を実行している箇所がいくつかあります。ここで子プロセスに介入しているようです。
推測は当たっていたようです。
procxmt( )のcase '7'について(子プロセスの本来の処理を続行させる方法について)
ptraceでrequest=7(requestはptrace( )の第一引数で、子プロセスにどの種類の処理をさせるかを示す)で子プロセスにシグナルをセットすることができます。この場合procxmt( )からは1が返り、stop( )からissig( )へと戻ります。
issig( )で、自分(子プロセス)が(u_signal[n]が偶数の)シグナルを受け取っているかをチェックし、その結果を返します。通常if( issig( ) ) psig( )という書き方をするので、シグナルはすぐに処理されます。
つまり、request=7でptrace( )を実行するとすぐにシグナル処理がされます。
ただしシグナル0(シグナルは受け取っていない)、もしくは、u_signal[n]が基数のシグナルがセットされるとpsig( )は実行されず子プロセスの本来の処理が続行されます。
cdbのソースを見るとrequest=7は以下のようにしか使われていません。
ptrace(CONTIN, pid, 0, 0); // CONTIN = 7
ptrace( )の第四引数がdata(signal#)で、これが0です。つまり、cdbの中ではrequest=7は子プロセスに処理を続行させる目的でしか使用していないようです。
トレースの解除について
request=8で子プロセスを終了できます。procxmt( )のcase 8を見るとexit( )を実行しているのがわかります。
cdbはこれを実行してあるプログラムのトレース解除(=そのプロセスを殺す)しているようです。トレースする必要なくなったプロセスを殺すというのはデバッガの設計を考えると自然だと思います。
終わりに
次回のLions本読書会は6月19日(日)に開催される予定です。「OSのコードを読んでみたい!」という方は参加してみてはいかがでしょうか。