LoginSignup
0
0

More than 1 year has passed since last update.

bashのサブシェルをgdbでデバッグするときの疑問

Last updated at Posted at 2021-08-07

この記事では疑問を書いています。
問題解決はしません。

以下のような問題がある。

親プロセスが、子プロセスの終了を待っているときに、子プロセスを追跡し、
親プロセスもデバッグしながら正常に動作させるにはどうすればいいのか?

以下のような単純なコマンドを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;

        /*forkしてサブシェルを生成*/
        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に返り値を返す(と思う)。


[追記]
gdbでサブシェルにアタッチしている間に、psコマンドで親子関係を確認しましたが、
サブシェルはbashの子プロセスのままでした。@angel_p_57さん、ありがとう。
(たぶん、gdbのinferiorコマンドについてもっと調べる必要がある。)


よって、もともとの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;
        }
    }
}

このような状況ではどのようにデバッグするべきなのか。

自分の考えは完全に間違っている可能性があるので、わかる方いればコメントいただけると
幸いです。

0
0
9

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0