bashが単純なコマンドのサブシェルを生成しないのはなぜですか?

bashが単純なコマンドのサブシェルを生成しないのはなぜですか?

次の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)

しかし、bash 3.Xでも同じ状況が観察されると思います。


より多くの例。組み込みコマンドを使用すると、サブシェルが生成されます。組み込み機能は親 bash では実行されません。

$ bash -c "read"

bash,100648
  └─bash,104336 -c read

sleeptop出力リダイレクトを使用して同じ動作を再現できます。

$ 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

答え1

最初の例では、PID 100648を持つシェルは対話型シェルであり、sleep実際には交換bash -c ''プロセスです。execve()システムコール呼び出された元のプロセスのPIDを保持する新しいプログラムを作成するため、元のプロセスはbash -c ''表示されません。シェルプロセスを単純なコマンドで置き換える簡単な理由は、次の説明に従ってリソースを節約するためです。スティーブンの答え到着単純なbashコマンドに明白な複製や分岐がないのはなぜですか?

これは、端末の簡単なテストでも確認できます。

$ echo $$
10250
$ bash -c 'sleep 60'

他の記事では:

$ pstree -p 10250
bash(10250)───sleep(21031)

なぜこのようなことが起こるのかはパイプもなく、簡単な命令しかないからです。bash 単純なコマンドの直接実行

このため、bash2番目の例では複数のステートメントがあることが認識されているため、sleep最初"sleep 10000; sleep 99999 "の場合はexecve()新しいプロセスが終了したときに親プロセスを置き換えることができます。問題ありません。ただし、ここでは最初のコマンドが完了した後にシェルを終了できません。したがって、sleep 10000出力で見ることができるのと同じフォークとexecveがあり、pstree後で新しいフォークとexecveがあります。sleep 99999


実行できるもう1つのテストは、単純なコマンドがシェル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

答え2

の出力は誤解を招く場合がpstreeあります。

bash -c "sleep 10000 "

Bashサブプロセスが実際に作成されました。ただし、プロセスは他のサブプロセスを生成しません。シェルを実行したsleep後は何もする必要はないので、最初にexecve()フォークせずにすぐに進むことができます。sleep

execve速度が速すぎるので、後でのみ結果を見ることができますpstree

しかし、

bash -c "sleep 10000; sleep 99999 "

新しいbashケースは、各コマンドに対して1回、2回フォークされます。execve最初に分岐するのではなく、最後のコマンドを実行することもできます。なぜできないのかわかりません。

このケースとリダイレクトのケースは、ブランチが必要な場合の検出の問題かもしれません。

関連情報