「サブシェル」と「子プロセス」の正確な違いは何ですか?


16

これこれによれば、サブシェルは括弧を使用して開始されます(…)

( echo "Hello" )

これこれ、およびこれによれば、コマンドが&

echo "Hello" &

Posix仕様ではsubshellこのページの単語を使用しています、このページでは定義されていません。また、同じページで「子プロセス」も定義されていません

両方ともfork()正しいカーネル機能を使用していますか?

いくつかのフォークを「サブシェル」と呼び、他のフォークを「子プロセス」と呼ぶ正確な違いは何ですか。


POSIX 理論的根拠をリンクする理由が明確ではない:ベース定義自体ではなくベース定義3.93子プロセス 「特定のプロセスによって(fork()、posix_spawn()、または...によって)作成された新しいプロセス」 ; 3.376サブシェル 「メインまたは現在のシェル実行環境と区別されるシェル実行環境」。したがって、同じ種類のインスタンスではありません。これはあなたが探している違いですか?
fra-san

@ fra-san A child processは、main次のような明確な環境を持つことができます( LANG=C eval 'echo "$LANG"' )。その子プロセス(括弧内)もサブシェル(異なる環境)ですか?
-NotAnUnixNazi

の式( )、定義上、独自の実行環境を持つサブシェルです。私のポイントは、サブシェルは子プロセスとして実装する必要がないということです(ステファンがksh93の例で彼の回答で指摘しているように)。以下のように見えますサブシェル子プロセスがあることを持っていない、両方の結果をfork()コール。したがって、2種類のフォークの違いを探すことは、私には正しい見方ではないようです。それが私があなたの質問をよりよく理解しようとした理由です。
fra san

ああ、リンク先のtldpページがサブシェル子プロセスであると実際に言っているのがわかります。私の意見では、定義は誤解を招く可能性のある単純化です。
fra-san

回答:


15

POSIX用語では、サブシェル環境はシェル実行環境の概念にリンクされています。

サブシェル環境は、親環境の複製として作成された別個のシェル実行環境です。その実行環境には、開かれたファイル、umask、作業ディレクトリ、シェル変数/関数/エイリアスなどが含まれます...

そのサブシェル環境を変更しても、親環境には影響しません。

従来、POSIX仕様のベースとなっているBourneシェルまたはksh88で、子プロセスをフォークすることによって行われていました。

POSIXがサブシェル環境でコマンドを実行することを要求または許可する領域は、伝統的にksh88が子シェルプロセスをforkした領域です。

ただし、実装に子プロセスを使用するよう強制することはありません。

代わりに、シェルはその個別の実行環境を好きなように実装することを選択できます。

たとえば、ksh93は、親の実行環境の属性を保存し、分岐を回避できるコンテキストでサブシェル環境の終了時にそれらを復元することでそれを行います(ほとんどのシステムでは分岐としての最適化が非常に高価です)。

たとえば、次の場所にあります。

cd /foo; pwd
(cd /bar; pwd)
pwd

POSIXではcd /foo、別の環境で実行する必要があり、次のような出力が必要です。

/foo
/bar
/foo

別のプロセスで実行する必要はありません。たとえば、stdoutが壊れたパイプにpwdなった場合、サブシェル環境で実行すると、SIGPIPEが唯一のシェルプロセスに送信されます。

を含むほとんどのシェルは、子プロセスbash内のコードを評価することで実装し(...)ます(親プロセスは終了を待機します)が、ksh93は代わりに(...)、すべて同じプロセス内でコードを実行すると、

  • サブシェル環境であることに注意してください。
  • cd、以前の作業ディレクトリ(通常はO_CLOEXECで開かれたファイル記述子に)を保存し、OLDPWD、PWD変数、およびcd変更する可能性のあるすべての値を保存してから、chdir("/bar")
  • サブシェルから戻ると、現在の作業ディレクトリが復元され(fchdir()保存されたfd上に)、サブシェルが変更した可能性のあるすべてのものが復元されます。

子プロセスを回避できない状況があります。ksh93はフォークしません:

  • var=$(subshell)
  • (subshell)

しかし、

  • { subshell; } &
  • { subshell; } | other command

つまり、物事を別々のプロセスで実行して、同時に実行できるようにする必要がある場合です。

ksh93の最適化はそれよりもさらに進んでいます。たとえば、

var=$(pwd)

ほとんどのシェルはプロセスを分岐し、子にpwdパイプにリダイレクトされたstdoutを使用してコマンドを実行させpwd、現在の作業ディレクトリをそのパイプに書き込み、親プロセスはパイプの反対側で結果を読み取り、ksh93どちらも仮想化しないフォークもパイプも必要ありません。フォークとパイプは、非組み込みコマンドにのみ使用されます。

シェルが子プロセスをフォークするサブシェル以外のコンテキストがあることに注意してください。たとえば、別の実行可能ファイル(同じシェルインタープリター用のスクリプトではない)に格納されているコマンドを実行するには、シェルはプロセスをフォークして、そのコマンドを実行する必要があります。そのコマンドが戻った後、さらにコマンドを実行できます。

に:

/bin/echo "$((n += 1))"

これはサブシェルではなく、コマンドは現在のシェル実行環境で評価され、現在のシェル実行環境のn変数がインクリメントされますが、シェルは子プロセスをフォークして、引数としてを/bin/echo展開してそのコマンドを実行し$((n += 1))ます。

多くのシェルは、スクリプトまたはサブシェル(子プロセスとして実装されるサブシェルの場合)の最後のコマンドである場合、その外部コマンドを実行するために子プロセスをフォークしないという最適化を実装します。(bashただし、そのコマンドがサブシェルの唯一のコマンドである場合にのみ実行されます)。

つまり、これらのシェルでは、サブシェルの最後のコマンドが外部コマンドである場合、サブシェルによって余分なプロセスが生成されることはありません。比較すると:

a=1; /bin/echo "$a"; a=2; /bin/echo "$a"

a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")

同じ数のプロセスが作成されますが、2番目の場合のみ、2番目のフォークがより早くa=2実行され、サブシェル環境で実行されます。


1

サブシェル

子シェルはサブシェルとも呼ばれます。サブシェルは、親シェルおよび別のシェルから作成できます。サブシェルは次を使用して作成できます。

1.プロセスリスト

プロセスリストは、括弧で囲まれたコマンドのグループです。例:

( pwd ; (echo $BASH_SUBSHELL)) 

これにより、現在の作業ディレクトリと生成されたシェルの数が出力されます。サブシェルの呼び出しは高価です。

2.コプロセス

バックグラウンドモードでサブシェルを生成し、そのサブシェル内でコマンドを実行します。

coproc sleep 10

jobsコマンドを入力した場合

[1]+  Running                 coproc COPROC sleep 10 &

スリープはバックグラウンドで実行されているバックグラウンドプロセスとして表示されます。

子プロセスのフォーク

コンピューティングの子プロセスは、別のプロセスによって作成されたプロセスです。外部コマンドが実行されるたびに、子プロセスが作成されます。このアクションはフォークと呼ばれます。

$ps -f
UID        PID  PPID  C STIME TTY          TIME CMD  
umcr7     3647  3638  0 13:54 pts/0    00:00:00 bash
umcr7     3749  3647  0 13:59 pts/0    00:00:00 ps -f

ps -f外部コマンドである(時々 、ファイルシステムコマンドと呼ばれる、すなわち、外部コマンドは、外部のbashシェルの存在するプログラムです。)これは、それが実行されるから、bashシェルの親IDと子プロセスを作成します。


0

両方(サブシェルと子シェル)は、親シェル(両方とも親シェルの子)とは別のプロセスです。つまり、PIDが異なります。そして、どちらも親シェルのフォーク(コピー)から始まります。

サブシェルは、親シェルのコピーであり、変数、関数、フラグ、およびすべてが、親シェルと同じように使用できます。このような値を変更しても、親には影響しません。

子シェルはforkとして起動しますが、start configsによって指定されたシェルのデフォルト値にリセットされます。これは、いくつかのコード(シェルまたはコマンド)を実行するために使用されるプロセスになります。

サブシェルは変数値にアクセスできます:

$ x=123; ( echo "$x")
123

子シェルはできませんでした(エクスポートされていない変数):

$ x=234; sh -c 'echo "x=$x"'
x=
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.