次の2つのbashコマンドについて考えてみます。 1つはサブシェルを作成し、もう1つは作成しません。
サブシェルなし
$ bash -c "sleep 10000 "
pstree出力:
bash,100648
└─sleep,103509 10000
サブシェル付き
$ bash -c "sleep 10000; sleep 99999 "
bash,100648
└─bash,103577 -c sleep 10000; sleep 99999
└─sleep,103578 10000
これはある種の最適化ですか? Bashはコマンドを確認し、単純なコマンドのプロセス作成をスキップします。単純なコマンドが親端末のbashによって生成されたように見えます。
私が使用しているBashバージョンは
$ bash --version
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
ただし、bash3.Xでも同じことが観察できると思います。
さらにいくつかの例。ビルトインを使用すると、サブシェルが生成されます。ビルトインは親bashでは実行されません。
$ bash -c "read"
bash,100648
└─bash,104336 -c read
sleep
での同じ動作は、top
と出力リダイレクトで再現できます。
$ bash -c "top -b "
bash,100648
└─top,104392 -b
そして
$ bash -c "top -b > /dev/null "
bash,100648
└─bash,104420 -c top -b > /dev/null
└─top,104421 -b
pstree
の出力は誤解を招く恐れがあります。
_bash -c "sleep 10000 "
_
bashの子プロセスを作成します。ただし、このプロセスは別の子プロセスを作成しません。 sleep
を実行した後は何もすることがないため、シェルは最初にフォークせずにexecve()
をsleep
に直接送信します。
これは非常に高速であるため、execve
のpstree
の後に結果が表示されます。
しかし、
_bash -c "sleep 10000; sleep 99999 "
_
新しいbashフォークをコマンドごとに1回ずつ、2回ケースに入れます。最初にフォークする代わりに、最後のコマンドにexecve
を作成することもできます。なぜそうならないのかわかりません。
これとリダイレクトの場合は、おそらくフォークが必要な場合の検出に関する問題にすぎません。
最初の例では、PID 100648のシェルがインタラクティブなシェルですが、sleep
は実際には_bash -c ''
_プロセスを置き換えたプロセスです。 execve()
syscall は、それを呼び出した元のプロセスのPIDを保持する新しいプログラムを生成します。したがって、元の_bash -c ''
_が表示されないのはなぜですか。単純なコマンドがシェルプロセスに置き換わる単純な理由は、 ステファンの答え で説明されているようにリソースを節約するためです。
これは、1つの端末での簡単なテストからも明らかです。
_$ echo $$
10250
$ bash -c 'sleep 60'
_
そして別の:
_$ pstree -p 10250
bash(10250)───sleep(21031)
_
このように発生する理由は、パイプのない単純なコマンドがあり、bash
単純なコマンドに対して直接実行を実行する であるためです。
まさにその理由で、bash
は、2番目の例に複数のコマンドステートメントがあることを認識します。したがって、_"sleep 10000; sleep 99999 "
_にあるsleep
コマンドごとに。最初のケースでは、execve()
は親プロセスを置き換えるだけで、新しいプロセスが終了したときに問題ありません。ただし、ここでは、最初のコマンドが完了した後、シェルを終了できません。したがって、_sleep 10000
_のforkとexecveがあります。これは、pstree
の出力に表示され、後で_sleep 99999
_の新しいforkとexecveが表示されます。
_bash -c ''
_の単純なコマンドがシェルプロセスを置き換えると信じるために実行できる別のテストは次のとおりです。
_$ bash -c 'grep "^Pid:" /proc/self/status /proc/$$/status'
/proc/self/status:Pid: 23946
/proc/23946/status:Pid: 23946
_
このような動作はbash固有であることに注意してください。 Debianベースのディストリビューションの_/bin/dash
_の場合(およびstrace
でも明らかです):
_$ sh -c 'grep "^Pid:" /proc/self/status /proc/$$/status'
/proc/self/status:Pid: 24188
/proc/24187/status:Pid: 24187
_