0

私はCで複数のパイプを実装しようとしていますが、解決策は次の両方である必要があります。

cmd1 | cmd2 | cmd3

および:

        |--- cmd2

cmd1    |--- cmd3

        |--- cmd4
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

int main(int argc, char *argv[]) {

char* args1[] = { "ls", NULL, NULL };
char* args2[] = { "ls", "-l", NULL };
char* args3[] = { "sort", NULL, NULL };
char* args4[] = { "wc", "-l", NULL };

int rc1 = execute_cmd(args1, 0);
//printf("rc1 = %d\n", rc1);

int rc2 = execute_cmd(args2, rc1);
//printf("rc2 = %d\n", rc2);

int rc3 = execute_cmd(args3, rc1);
//printf("rc3 = %d\n", rc3);

int rc4 = execute_cmd(args4, rc1);
//printf("rc4 = %d\n", rc4);

int buffer[1024];
int len = 0;

if (rc2) {
    while ((len = read(rc2, buffer, sizeof(buffer))) > 0) {
        write(STDERR_FILENO, "rc2\n", 4);
        write(STDERR_FILENO, &buffer, len);
    }
} else {
    printf(stderr, "ERROR\n");
}

if (rc3) {
    while ((len = read(rc3, buffer, sizeof(buffer))) > 0) {
        write(STDERR_FILENO, "rc3\n", 4);
        write(STDERR_FILENO, &buffer, len);
    }
} else {
    printf(stderr, "ERROR\n");
}

if (rc4) {
    while ((len = read(rc4, buffer, sizeof(buffer))) > 0) {
        write(STDERR_FILENO, "rc4\n", 4);
        write(STDERR_FILENO, &buffer, len);
    }
} else {
    printf(stderr, "ERROR\n");
}

return 0;
}

int execute_cmd(char** args, int fd_in) {

int pipefd[2];
pipe(pipefd);

if (fork() == 0) {
    close(pipefd[0]);

    dup2(pipefd[1], STDOUT_FILENO);
    dup2(pipefd[1], STDERR_FILENO);

    close(pipefd[1]);

    if (fd_in) {
        dup2(fd_in, 0);
    }

    execvp(*args, args);
    printf("failed to execute %s %s", *args, *args[0]);
} else {
    close(pipefd[1]);

    return pipefd[0];

}
}

正しい結果を確認した後と、別の結果を確認した場合、プログラムの出力は決定論的ではありません。dup2が複数回実行され、結果のファイル記述子から読み取られたファイル記述子ごとに、dup2が期待どおりに機能しないように見えます-コピーされたファイル記述子に影響を与えているように見えますか?

私が設計で述べたように機能する場合、両方に使用する必要があるシステムコールはどれですか?

4

2 に答える 2

2

Yes, dup and dup2 create completely equivalent handles to the same pipe. If several processes (or threads) simultaneously attempt to read from the pipe using duplicated/forked descriptors, a "random" of them will get to the data first, but each byte written to the pipe only gets delivered once.

If you want to copy data to multiple different readers, you have to program that explicitly -- fork a subprocess (or spawn a thread) to read some data from one incoming pipe, then write it to all of the outgoing ones, and continue in a loop until you reach EOF.

于 2011-08-07T05:52:54.017 に答える
0

同じパイプへの複数のハンドル/参照があると、同期などで多くの問題が発生します。

たとえば、2つの子プロセスがあり、一方が「Hello \ n」、次に「World \ n」を送信し、もう一方が「Foo \ n」、次に「Bar\n」を送信する場合。その場合、「Hello \ n World \ n Foo \ n Bar\n」または「Hello\nFoo \ n World \nBar」または「Foo\nHello \ n Bar \nWorld」などになります。出力は順序付けられていない状態になります(これは非常に混乱します)。

解決策は、異なるパイプを使用することです。

基本的に、メインプログラムがフォークするとき、子プロセスのSTDOUTおよびSTDERRになる新しいパイプを作成する必要があります。次に、メインプログラムは、すべての新しいパイプの最後から読み取り、(潜在的に)情報をバッファリングする必要があります。これにより、メインプロセスは、子から独自のSTDOUT / STDERRに特定の順序でデータを送信できます(たとえば、すべての最初の子の出力、次にすべての2番目の子の出力、次にすべての次の子の出力など。

メインプログラムは、追加情報を追加し、何が起こっているのかを明確にするためにいくつかのフォーマットを行うこともできます。上記の例では、次のようになります。

Process A (exit status = 0, OK):
    Hello
    World
Process B (exit status = 1, Failed):
    Foo
    Bar

だけでなく:

 Hello
 World
 Foo
 Bar

入力(STDIN)については、どのように機能させたいのかわかりません。子プロセスのいずれもSTDINを必要としない場合(最も簡単で最も可能性の高いケース)、それを無視できます。各子プロセスがメインプロセスのSTDINの独自のコピーを取得する必要がある場合は、各子がSTDINとして使用するための新しいパイプを作成する必要があります。

もう1つの方法は、「現在選択されている子」を作成することです。これは、はるかに複雑になる可能性があります(特に、選択された子の出力を表示できる必要があるエンドユーザーがいる場合は、何らかの方法で実装する必要があります)。 「現在表示されている」子を切り替える方法-たとえば、ユーザーが別の子を選択した場合は、画面をクリアしてその子のバックログを表示します)。

于 2011-08-07T06:33:52.290 に答える