web-dev-qa-db-ja.com

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

this および this によると、サブシェルは括弧_(…)_を使用して開始されます。

_( echo "Hello" )
_

thisthis および this に従って、コマンドが_&_で開始されると、プロセスが分岐します。

_echo "Hello" &
_

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

どちらもカーネルfork()関数を使用していますか?

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

16
Isaac

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

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

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

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

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

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

$ x=234; sh -c 'echo "x=$x"'
x=
0
Isaac

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

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

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

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

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

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

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

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

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

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

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

/foo
/bar
/foo

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

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

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

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

  • var=$(subshell)
  • (subshell)

しかし、

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

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

ksh93の最適化はそれ以上に進んでいます。たとえば、

var=$(pwd)

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

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

に:

/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がサブシェル環境で実行されます。

15

サブシェルは、親シェル(通常は子プロセスで実行される)のコピーです。

子プロセスは、コード(シェル、サブシェル、またはコマンド)を実行するために呼び出されるプロセスです。

  1. サブシェルの明確な例は、_( … )_呼び出しでシェルが(通常)行うことです。

    • 親シェルをフォークし、サブシェルを作成します
    • サブシェルは_( … )_内のコードを実行します
    • サブシェルは、終了コード、stdoutおよびstderrを親シェルに返します。
    • 親シェルは、終了コード、stdoutおよびstderr文字列を処理します。

    一部のシェルは、サブシェルなしで(より高速に)_( … )_(または$(…))への呼び出しを実装する場合があります。

    _$ seq 1000000 > file
    
    $ time ksh -c 'var=$(<file)'
    run    : 0.462s sec
    
    $ time zsh -c 'var=$(<file)'
    run    : 2.037s sec
    _
  2. 子プロセスは、(ほとんど)外部コマンドが呼び出されたときに開始されます。

    _$ ls
    _

    コマンドが実行されるプロセスです。

2
Isaac

サブシェル

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

1。プロセスリスト

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

( pwd ; (echo $BASH_SUBSHELL)) 

これにより、現在の作業ディレクトリと生成されたシェルの数が出力されます。 [〜#〜] note [〜#〜]サブシェルの呼び出しにはコストがかかります。

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を持つ子プロセスが作成されます。