web-dev-qa-db-ja.com

なぜ(...)バックグラウンドで実行したときに新しい子プロセスが生成されないのですか?

コマンドを実行した後

{ sleep 5; } &  

psの出力は(出力1)です

 PID TTY           TIME CMD
  972 ttys000    0:00.27 -bash
 2556 ttys000    0:00.00 -bash
 2557 ttys000    0:00.00 sleep 5   

の間

( sleep 5 ) &    

out of ps is(output 2)

PID TTY           TIME CMD
 972 ttys000    0:00.28 -bash
2566 ttys000    0:00.00 sleep 5  

()はサブシェル環境を引き起こし、現在のシェルで実行される{ sleep 5; } &の「出力2」を期待しながら子プロセスをフォークするため、この場合は「出力1」を期待します。これはばかげた質問に見えるかもしれませんが、私はこの振る舞いを本当に理解していません。
ここで何が欠けていますか?

5
haccks

Bashは、サブシェルの最後のコマンドまたは唯一のコマンドを実行します with exec 最適化として安全に実行できることがわかった場合。 pstreeでこれを明確に検証できます:

$ pstree $$
bash---pstree
$ ( pstree $$ )
bash---pstree
$ ( pstree $$ ; echo)
bash---bash---pstree

$ 

そのため、( sleep 5 )sleepコマンドとしてのみ表示され、中間シェルは表示されません。上記の最後の例では、echopstreeの完了後にシェルに何かを強制するため、実際に使用するシェルがあります。中間のケースでは、生成されたシェルはすぐにexecs pstreeになるため、最初のケースと同じように見えます(これは標準的な fork-exec )。多くのシェルがこれを行います。


一方、バックグラウンドで実行するものは、新しいプロセスを生成する必要があります:基本的に、それはそれです「背景」です。中かっこdo内のコマンドは通常、現在のシェルで実行されますが、バックグラウンドで実行する場合は実行できません。親シェルがバックグラウンドコマンドの実行中に何をしていても続けることができるように、{ ... }全体を実行するために新しいシェルを作成する必要があります。

$ { pstree $$ ; }
bash---pstree
$ { pstree $$ ; } &
bash---bash---pstree

この場合、後続のコマンドがあるかどうかに関係なく、違いはありません。 Bashは& のこの動作を文書化します:

コマンドが制御演算子 ‘&’によって終了された場合、シェルはコマンドをサブシェルで非同期的に実行します。これは、コマンドをバックグラウンドで実行することとして知られています。シェルはコマンドが完了するまで待機せず、戻りステータスは0(真)です。

read =のような組み込みコマンドをバックグラウンド化することで、これが行われていることも確認できます。

$ read &
$ pstree $$
[1] 32394
[1]+  Stopped                 read
$ pstree $$
bash-+-bash
     `-pstree

私のシェルにはtwoの2つの子があります:bash実行中のreadと、その出力を出力するpstreeコマンド。


シェルがさらに最適化を行い、それをバックグラウンドの{ ... }に適用できることは事実ですが、括弧で囲まれたサブシェルの場合と同じですが、そうではありません。他のいくつかのシェルでも可能ですが、簡単なテストでは見つかりませんでした。これはかなりまれなケースであり、正式には異なり、いくつかの奇妙なコーナーケースがあります。

11
Michael Homer