パイプの一端にfdの読み取りと書き込みの両方がありますか?

パイプの一端にfdの読み取りと書き込みの両方がありますか?

私が知っている限り、パイプの一端はfdを読み書きすることができ、もう一方の端はfdを読み書きできます。これが私たちがusingを使って書き込むときにパイプの同じ側の読み出し端を閉じ、fd[1]usingを使って2番目の端から読み取るときにその端の読み出し端を閉じる理由です。私は正しいですか?fd[0]fd[0]fd[1]

答え1

はい、パイプライン()で作成されたパイプラインには2つのファイル記述子があります。fd[0]読み書きfd[1]用。

いいえ、パイプの両端を閉じる必要はありません。双方向通信に使用できます。

編集:コメントでこれがどのようなものに関連しているのか疑問に思いましたので、ls | lessこれについても説明します。

シェルには、0(stdin)、1(stdout)、2(stderr)の3つのオープンファイル記述子があります。シェルがコマンドを実行すると、次のことが行われます(少し単純化されました。

pid = fork();
if(pid == 0) {
    /* I am the child */
    execve(...); /* Whatever the user asked for */
}
else {
    waitpid(pid); /* Wait for child to complete */
}

ファイル記述子0、1、2は子プロセスによって継承されるため、入力/出力は期待どおりに機能します。これにより、ls | lessリダイレクト時に若干異なることが発生します。

int pipe[2];
pipe(pipe, 0);

pid1 = fork();
if(pid1 == 0) {
    /* This is ls, we need to remap stdout to the pipe. We don't care about reading from the pipe */
    close(pipe[0]);
    close(1);
    dup2(pipe[1], 1);
    execve(...);
}
else {
    pid2 = fork();
    if(pid2 == 0) {
        /* This is less, it reads from the pipe */
        close(pipe[1]);
        close(0);
        dup2(pipe[0], 0);
        execve(...);
    }
    else {
        waitpid(pid1);
        waitpid(pid2);
    }
}

したがって、シェルはパイプを作成して分岐し、実行前にパイプをサブプロセスのstdinまたはstdoutに再マップして、データがプロセス1からプロセス2に流れることができます。シェルパイプは双方向ではないため、パイプの一方の端だけを使用し、もう一方の端を閉じます(ファイル記述子を標準入力または標準出力にコピーした後、実際にもう一方の端も閉じます)。

答え2

まとめですチャット会話 デニス他の初心者でも簡単に使えるように簡素化しました。実行時ls | less

  1. bashシェルは、およびの親fd[0]プロセスfd[1]ですfd[2]
  2. pipe()親によって呼び出されると、データを保持するためのバッファにすぎないパイプを作成し、各末尾に読み取り用と書き込み用のファイル記述子があります。
  3. 子プロセスは、開いているすべてのfd 0、1、2、およびパイプ用の2つを継承します。したがって、lsfdsは両方とも独自のバッファコピーを持っていますが、両方ともfdsの同じパイプを指します。最初の子プロセス内で実行すると、出力をパイプ()の書き込みの終わりに再マップしてからコンソールに再マップする必要があります。だから、再マッピングがあるでしょう。親プロセスではなく子プロセスにのみ影響します。これで、出力がサブパイプの書き込みの終わりに渡されます。それでも読み終わりであり、データ自体を読みたくないので、最後に閉じる必要があります。繰り返しますが、これは。fd[0]fd[1]bashlsfd[1]close(1)dup(fd[1])close(1)lsbashlsfd[1]fd[0]lsclose(fd[0])lsbash
  4. lessパイプファイル記述子を継承するサブプロセスを作成するために再分岐します。入力はlessバッファから読み取る必要があるため、標準入力ではなくなり、再マッピングする必要があるため、入力fd[0]は書き込みの終わりを読み取って閉じます。書き込んだデータが書き換えられないようにするために必要です。fd[0]close(0)dup(fd[0])fd[0]lslessfd[0]fd[1]less

答え3

lessコマンドを呼び出す前に、close親でfd [1]を実行する必要があります。それ以外の場合、コマンドは入力を無限に待機します。fork()less

関連情報