Bashで外部コマンド実行のトレース出力を理解する方法は?

Bashで外部コマンド実行のトレース出力を理解する方法は?

Ubuntuはdatepid 6913を使用して対話型bashシェルで直接実行します。

$ date
Wed Mar  2 23:57:44 EST 2016

その間、次のコマンドを使用して、他の対話型bashシェルでbashシェル6913を追跡していますstrace

    $ sudo strace -f -e trace=process -p 6913
    Process 6913 attached
    clone(Process 9098 attached
    child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
    child_tidptr=0x7f457c05ca10) = 9098
    [pid  6913] wait4(-1,  <unfinished ...>
    [pid  9098] execve("/bin/date", ["date"], [/* 66 vars */]) = 0
    [pid  9098] arch_prctl(ARCH_SET_FS, 0x7f40d6a4f740) = 0
    [pid  9098] exit_group(0)               = ?
    [pid  9098] +++ exited with 0 +++
    <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WSTOPPED|WCONTINUED, NULL) = 9098
    --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=9098, si_status=0, si_utime=0, si_stime=0} ---
    wait4(-1, 0x7ffea6781518, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD (No child processes)
    clone(Process 9099 attached
    child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 9099
    [pid  9099] clone(Process 9100 attached
    child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 9100
    [pid  9099] wait4(-1,  <unfinished ...>
    [pid  9100] execve("/bin/sed", ["sed", "s:\\([^/]\\)[^/]*/:\\1/:g"], [/* 66 vars */]) = 0
    [pid  9100] arch_prctl(ARCH_SET_FS, 0x7f998bb03840) = 0
    [pid  9100] exit_group(0)               = ?
    [pid  9100] +++ exited with 0 +++
    [pid  9099] <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 9100
    [pid  9099] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=9100, si_status=0, si_utime=0, si_stime=0} ---
    [pid  9099] wait4(-1, 0x7ffea6780c58, WNOHANG, NULL) = -1 ECHILD (No child processes)
    [pid  9099] exit_group(0)               = ?
    [pid  9099] +++ exited with 0 +++
    --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=9099, si_status=0, si_utime=0, si_stime=0} ---
    wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, NULL) = 9099
    wait4(-1, 0x7ffea6780f18, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD (No child processes)

私が見るには、出力が2つの部分に分けられるようです。

  1. まず、bashシェル(6913) clone()自体が子プロセス9098を作成してから、子プロセス9098をexecve() date終了します。

  2. その後、bashシェル(6913)clone()自体は子プロセス(9099)を作成し、それ自体はclone()子プロセス(9100)を作成し、その後子プロセス(9100)を作成しますexecve() sed私の質問2番目の部分について:

    • 最後の2つとclone()最後の1つは、execve() bashシェル6913から受信したSIGCHLDを処理するジョブに属していますか?これは何をしますか?

    • 9100を処理する理由は何ですかexecve() sed?ここで何をしているのsed

    • プロセス9099がなぜそのプロセスではないのですかexecve() sed? 9099がclone()9100を作成してから9100を作成するのはなぜですかexecve() sed?つまり、1つのクローン9099の代わりに2つの連続クローン9099と9100が必要なのはなぜですか?


コメントに返信:

$ echo $PROMPT_COMMAND
pwd2=$(sed "s:\([^/]\)[^/]*/:\1/:g" <<<$PWD)
$ echo $PS1
\u@\h:$pwd2\$

shel 6913で実行した後、トレースunset PROMPT_COMMAND出力は次のようになります。

$ sudo strace -f -e trace=process -p 6913
[sudo] password for t: 
Process 6913 attached
clone(Process 12918 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 12918
[pid  6913] wait4(-1,  <unfinished ...>
[pid 12918] execve("/bin/date", ["date"], [/* 66 vars */]) = 0
[pid 12918] arch_prctl(ARCH_SET_FS, 0x7ff00c632740) = 0
[pid 12918] exit_group(0)               = ?
[pid 12918] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WSTOPPED|WCONTINUED, NULL) = 12918
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12918, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, 0x7ffea6781518, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD (No child processes)

さて、上記の最初の2つの質問に答えがあります。 3番目の質問はまだよくわかりません。

答え1

clone(Process 9099 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 9099
[pid  9099] clone(Process 9100 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f457c05ca10) = 9100
[pid  9099] wait4(-1,  <unfinished ...>
[pid  9100] execve("/bin/sed", ["sed", "s:\\([^/]\\)[^/]*/:\\1/:g"], [/* 66 vars */]) = 0

この2つのフォーク(最新のLinuxシステムではシステムコールを使用してフォークが行われます)は、bashが変数をclone評価するためです。PROMPT_COMMAND

pwd2=$(sed "s:\([^/]\)[^/]*/:\1/:g" <<<$PWD)

これらのフォークは、以前に受信したSIGCHLD信号と直接関係しません。

これバッシュマニュアル説明する:

Bashは、各デフォルトプロンプトを印刷する前に、PROMPT_COMMAND変数の値を確認します。 PROMPT_COMMANDが設定され、NULL以外の値を持つ場合、値はコマンドラインに入力されているかのように実行されます。

内部的にbashは最終的に呼び出します。解析と実行内容を評価しますPROMPT_COMMAND。 Bashは、実行する必要があるフォークの数を最小限に抑えるように努めています。のような単純な文の場合、pwd2=$PWD分岐は必要ありません。より複雑なステートメントの場合は、1つ以上のフォークを作成できます。あなたの場合、$( ... )シェルフォーク(pid 9099)が発生し、角かっこ間のコマンドが評価されます。組み込みのユーティリティを呼び出すと、sed別のフォークであるpid 9100が作成され、その後にexecveが続きます/bin/sed

sedはここで何をしていますか?

現在の作業ディレクトリの上のすべてのディレクトリ名から、最初の文字を除くすべての文字が切り捨てられるようです。

関連情報