bashのサブシェルをgdbでデバッグするときの疑問
解決したいこと
親プロセスが、子プロセスの終了を待っているときに、子プロセスを追跡し、
親プロセスもデバッグしながら正常に動作させるにはどうすればいいのか?
発生している問題・エラー
以下のような単純なコマンドをbashに実行させたときのbashの挙動をgdbでデバッグしている。
$ (echo hello)
このコマンドはbashにサブシェルをsyncronus(同期的)で立ち上げるように指示し、その中でビルトインコマンドのechoを実行させる。
syncronusとは親プロセスが、子プロセスの終了を待つことを意味する。
このコマンドが処理されるのはexecute_cmd.cのexecute_command_internal()という関数の中だ。
かなり端折っているが、関数は以下のようになっている。
int execute_command_internal(...)
{
if (command->type == cm_subshell || /*いろいろ評価*/) {
pid_t paren_pid;
paren_pid = make_child();
/*ここは子プロセスに実行される*/
if (paren_pid == 0) {
last_command_execute_value = execute_in_subshell()
/*後始末をして、親プロセスに値を返す*/
subshell_exit();
}
/*ここは親プロセスに実行される*/
else {
/*assyncronus == 0 は syncronusを意味する。*/
if (assyncronus == 0)
/*子プロセスを待つ*/
wait_for(paren_pid)
else
return EXECUTION_SUCCESS;
}
}
}
上のコードはだいたい以下のような処理をしている。
bashがコマンドをsyncronusで実行するように認識すると、
まずbashはforkしてサブシェルを作る。
bash(親プロセス)はwait_for(parent_pid)を実行し、
サブシェル(子プロセス)の終了を待つ。
サブシェルは内部でコマンドを実行し、
結果を親プロセスに返す。
サブシェルの終了がわかると、親プロセスであるbashは処理を続ける。
gdbでは以下のような設定を入力している。
/*forkした子プロセスを追跡するようにgdbに指示する*/
(gdb) set follow-fork-mode child
/*forkしたとき、通常はどちらかのプロセスの制御を手放すが、この入力をすることで両方のプロセスの制御を続ける。*/
(gdb) set detach-on-fork off
しかし、このようにすると、サブシェルはgdbの子プロセスになってしまい
サブシェルの実行が終了すると、gdbに返り値を返す(と思う)。
よって、もともとのbashはwait_for(child)をしているが、子プロセスは
すでにgdbに値を返しているので、wait_for()が完了することは永遠にない。
上記のcのコードにコメントをつけ直すと以下のようになる。
int execute_command_internal(...)
{
if (command->type == cm_subshell || /*いろいろ評価*/) {
pid_t paren_pid;
paren_pid = make_child();
/*ここはサブシェルに実行される。*/
if (paren_pid == 0) {
last_command_execute_value = execute_in_subshell()
/*サブシェルはgdbの子プロセスになっているので、サブシェルはgdbに値を返す。*/
subshell_exit();
}
/*ここは親プロセスに実行される*/
else {
/*assyncronus == 0 は syncronusを意味する。*/
if (assyncronus == 0)
/*プロセスを待つがフォークしたサブシェルはもはやbashの子プロセスではなくなっている。
よって値が返されることはなく、bashはここで永遠に待ち続ける。*/
wait_for(paren_pid)
else
return EXECUTION_SUCCESS;
}
}
}
このような状況ではどのようにデバッグするべきなのか。
知っている方は教えていただけると幸いです。