6

次のコードがあるとします。

int main(int argc, char *argv[])
{
    int pipefd[2];
    pid_t cpid;
    char buf;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s \n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    cpid = fork();
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (cpid == 0) {    /* Child reads from pipe */
        close(pipefd[1]);          /* Close unused write end */

        while (read(pipefd[0], &buf, 1) > 0)
            write(STDOUT_FILENO, &buf, 1);

        write(STDOUT_FILENO, "\n", 1);
        close(pipefd[0]);
        _exit(EXIT_SUCCESS);

    } else {            /* Parent writes argv[1] to pipe */
        close(pipefd[0]);          /* Close unused read end */
        write(pipefd[1], argv[1], strlen(argv[1]));
        close(pipefd[1]);          /* Reader will see EOF */
        wait(NULL);                /* Wait for child */
        exit(EXIT_SUCCESS);
    }
return 0;

}

子プロセスがパイプから読み取りたいときはいつでも、最初にパイプ側を書き込みから閉じる必要があります。close(pipefd[1]);子プロセスからその行を削除するとif、基本的に「わかりました、子はパイプから読み取ることができますが、親が同時にパイプに書き込むことを許可しています」と言っていますか?

もしそうなら、パイプが読み取りと書き込みの両方で開かれているとどうなりますか? 相互排除はありませんか?

4

4 に答える 4

18

子プロセスがパイプから読み取りたいときはいつでも、最初にパイプ側を書き込みから閉じる必要があります。

プロセス (親または子) がパイプの書き込み側を使用しない場合は、そのファイル記述子を閉じる必要があります。パイプの読み取り側についても同様です。システムは、現在パイプから読み取ろうとしているプロセスが唯一のプロセスである場合でも、任意のプロセスが書き込み終了を開いている間に書き込みが発生する可能性があると想定し、システムは EOF を報告しません。さらに、パイプをいっぱいにしすぎて、読み取り側が開いているプロセスがまだある場合 (そのプロセスが書き込みを試みているプロセスであっても)、書き込みはハングし、書き込みが完了するためのスペースがリーダーによって確保されるのを待ちます。

その行を削除すると close(pipefd[1]); 子のプロセスIFから、私は基本的に「わかりました、子はパイプから読み取ることができますが、親が同時にパイプに書き込むことを許可しています」と言っていますか?

いいえ; あなたは、子が親と同様にパイプに書き込むことができると言っています。パイプの書き込みファイル記述子を持つすべてのプロセスは、パイプに書き込むことができます。

もしそうなら、パイプが読み取りと書き込みの両方に対して開かれている場合、つまり相互排除がない場合はどうなるでしょうか?

相互排除はこれまでありません。パイプ書き込み記述子が開いているプロセスは、いつでもパイプに書き込むことができます。カーネルは、2 つの同時書き込み操作が実際にシリアライズされることを保証します。パイプ読み取り記述子が開いているプロセスは、いつでもパイプから読み取ることができます。カーネルは、2 つの同時読み取り操作が異なるデータ バイトを取得することを保証します。

1 つのプロセスだけが書き込み用に開いており、1 つのプロセスだけが読み取り用に開いていることを確認することで、パイプが単方向で使用されるようにします。ただし、それはプログラミング上の決定です。書き込み側が開いている N 個のプロセスと、読み取り側が開いている M 個のプロセスを持つことができます (そして、N 個のプロセスのセットと M 個のプロセスのセットの間に共通のプロセスが存在する可能性があります)。驚くほど正気で働くために。しかし、データのパケットが書き込まれた後にどこで読み取られるかを簡単に予測することはできません。

于 2012-07-22T15:28:22.993 に答える
3

fork() はファイル ハンドルを複製するため、パイプの両端に 2 つのハンドルがあります。

さて、これを考えてみましょう。親がパイプの未使用の端を閉じない場合でも、2 つのハンドルが存在します。子が死亡した場合、子側のハンドルはなくなりますが、親が保持している開いたハンドルが残っています。したがって、パイプがまだ完全に有効であるため、「壊れたパイプ」または「EOF」が到着することはありません。もう誰もデータを入れません。

もちろん反対方向も同じです。

はい、親/子はハンドルを使用して独自のパイプに書き込むことができます。ただし、これのユースケースは覚えていませんが、それでも同期の問題が発生します。

于 2012-07-22T10:31:12.630 に答える
1

SunOSのpipe()のマニュアルページ:-片方の端だけ(すべての書き込みファイル記述子が閉じている)の空のパイプ(バッファリングされたデータなし)での読み取り呼び出しは、EOF(ファイルの終わり)を返します。

 A SIGPIPE signal is generated if a write on a pipe with only
 one end is attempted.
于 2012-09-15T20:54:35.863 に答える
1

パイプが作成されると、読み取り側と書き込み側の 2 つの端があります。これらは、ユーザー ファイル記述子テーブルのエントリです。

同様に、File テーブルには、読み取り側と書き込み側の両方の参照カウントとして 1 を持つ 2 つのエントリがあります。

フォークすると、ファイル記述子が複製された子が作成されるため、ファイルテーブルの両端の参照カウントは2になります。

"When I remove that line close(pipefd[1])" -> この場合、親が書き込みを完了した場合でも、この行の下の while ループは、読み取りが 0 (つまり EOF) を返すまでブロックされます。これは、親が書き込みを完了してパイプの書き込み側を閉じたとしても、File テーブルの書き込み側の参照カウントがまだ 1 (最初は 2 だった) であるため、読み取り関数がまだデータを待っているために発生します。決して起こらない到着。

「close(pipefd[0]);」と書いていない場合 親では、親で一度書いているので、この現在のコードは問題を示さないかもしれません。

ただし、複数回書き込む場合、理想的にはエラーを取得したいと考えていましたが (子が読み取っていない場合)、親の読み取り終了が閉じられていないため、エラーは発生しません (子供は読むためにもうそこにいません)。

したがって、データの読み取り/書き込みを継続的に行うと、未使用の端を閉じないという問題が明らかになります。これは、データの読み取り/書き込みを 1 回だけ行っている場合には明らかではない可能性があります。

子の読み取りループの代わりに、以下の行を 1 回だけ使用し、一度にすべてのデータを取得し、EOF のチェックを気にしない場合のように、「閉じる (pipefd[1]);" 子供の中で。

read(pipefd[0], buf, sizeof(buf));//buf is a character array sufficiently large  
于 2012-07-22T13:55:23.827 に答える