lsコマンドでexec()の後にどうなりますか?親プロセスはコンソールに出力を印刷しますか、それとも子プロセスに印刷しますか?

lsコマンドでexec()の後にどうなりますか?親プロセスはコンソールに出力を印刷しますか、それとも子プロセスに印刷しますか?

コマンドの実行に関する簡単な質問がありますls。インターネットで調査した内容に基づいて、次のことを理解しています。

  1. コマンドを入力すると、シェルはlsコマンドを解釈します。

  2. その後、シェルプロセスは子プロセスを分岐して生成し、親プロセス(シェル)はシステムコールを実行し、wait()子プロセスが終了するまで効果的にスリープモードになります。

  3. 子プロセスは、開いているすべてのファイル記述子と環境を継承します。

  4. サブプロセス(シェル)はプログラムの一部を実行し、バイナリファイルがディスクexec()(ファイルシステム)からロードされ、ls同じlsプロセスで実行されるようにします。

  5. lsプログラムの実行が完了するとexit()呼び出され、カーネルは子プロセスが終了したことを示すシグナルを親プロセスに送信します。

私の疑いは、lsタスクが完了したら結果を親プロセスに送り返すのか、それとも出力を画面に表示するのですか?出力を親項目に戻す場合はpipe()暗黙的に使用されますか?

答え1

ls出力すべきことを出力します標準出力。これを行うには、write次のシステムコールを呼び出します。

 write(1, "file1  file2...\n", 16)

(または同様の関数を呼び出すか、最終的にシステムコールを実行する可能性が高いlibcprintffwritewrite()

ファイル記述子1(規則に従ってstdout)がすでに開いていて何かを指しているとします。実際にlsファイル記述子1が端末などを指していることを確認してください。端末を指していない場合は、端末を指します。

 write(1, "file1\nfile2...\n", 15)

つまり、出力が端末に送信されない場合は、1行に1行ずつファイルに書き込みます。

あなたが書くとき:

 ls file1 file2

lsファイル記述子 1 は、シェルの fd 1 と同じリソースを指します (たとえば、 で始まる対話型シェルの場合、xtermxterm によって制御される擬似端末装置を指します)。シェルは特別な操作を行わずに継承され、forkそのO_CLOEXECフラグは通常ファイル記述子に対して設定されていないためexecve

あなたが書いている場合:

var=$(ls file1 file2)

シェルはパイプを作成し、サブプロセスのfd 1をパイプの書き込み端に割り当て、パイプのもう一方の端を読み取り、変数を埋めますvar

終了時に魔法のように実行されるのではなく、プロセス操作の一部として実行されます。これはシェルの他の活動とは無関係です。一部のリソース(たとえば、端末)に1つの接続がlsある別のプロセスであり、シェルは実行中です。fdwaitpid()

ただし、stdoutが端末でない場合は、出力をバッファリングして十分なデータ(十分なキロバイト)を蓄積した場合、または出力を閉じるか終了するときにのみls呼び出されることがわかります。write()したがって、終了時に完了を見つけることができますが、writeこれらのI / Oライブラリが完了するバッファの一部としてのみ可能です。flushing

答え2

通常、親プロセスは子プロセスが終了するのを待ちます。waitpid。親プロセスはプロセスのPIDを取得します。fork

これは、子プロセスが何らかの方法で親プロセスに、自分が終了したかどうかを通知しないことを意味します。これは、サブプロセスではなくシステムによって実行されます。

プログラムの出力について話している場合、親プロセスはfdsを提供しない限り、通常は子プロセスの出力を受け取りません。これはまた、子プロセスが出力を印刷しますいいえ親プロセス。親プロセスはプロセス状態に関する情報のみを受け取ります。詳細については、次を参照してください。waitpidマニュアルページ)

答え3

厳密に言えばどちらもありません親と子の実際の出力は画面に移動し、代わりにオペレーティングシステムカーネル(特にttyドライバ)が出力を実行します。プロセスはファイル記述子にのみデータを送信します。

strace -f /bin/bash "ls;exit"また、以下のように、ファイル記述子に誰が何を書いているのかを明確に推測しないでください(BASHは分岐しないことで「-c ls」を最適化するようで、後で「;exit」を使用しました)。 (bashcloneはnoを使用しますfork):

...
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f9814b5d9d0) = 30029
Process 30029 attached
[pid 30028] rt_sigprocmask(SIG_SETMASK, [],  <unfinished ...>
[pid 30029] rt_sigprocmask(SIG_SETMASK, [],  <unfinished ...>
...
[pid 30028] wait4(-1,  <unfinished ...>
...
[pid 30029] execve("/usr/bin/ls", ["ls"], [/* 72 vars */]) = 0
...
[pid 30029] write(1, "00708022-PTF-26962\nCASE00280714\n"..., 18500708022-PTF-26962) = 185
...
[pid 30029] exit_group(0)               = ?
[pid 30029] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 30029
...

したがって、実際にはPID 30029(サブプロセス)がファイル名を書き、終了してから親プロセスが続くことがわかります(以降wait4)。

答え4

成功すると、exec(3)それを呼び出したプログラムはもう存在しません。プログラムエド(program ed)に置き換えられましたexec。実行する新しいプログラムは、元のプログラム、特に開いているファイルの環境を継承します。

関連情報