0

2 つのプロセス (「ls」プロセスと「grep」) があります。私はパイプを使用して両者の間で通信しています。しかし、grep プロセスはパイプから読み取ることができません。その理由を理解するのを手伝ってもらえますか?

これが私のコードです

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int pipe_fd[2];

int main()
{
    pid_t p1,p2;
    char *prog1_argv[4];
    char *prog2_argv[2];
    /* Build argument list */
    prog1_argv[0] = "ls";
    prog1_argv[1] = "-l";
    prog1_argv[2] = "/";
    prog1_argv[3] = NULL;
    prog2_argv[0] = "grep";
    prog2_argv[1] = "s";
    prog2_argv[1] = NULL;
    if (pipe(pipe_fd) < 0)
    {
        printf ("pipe failed");
    }
    p1 = fork();
    if(p1 == 0)
    {
        printf("in child\n");
        close(pipe_fd[0]);
        if(dup2(pipe_fd[1],1)<0)
        {
            printf("dup failed:%d\n",errno);
        }
        close(pipe_fd[1]);
        if(execvp (prog1_argv[0], prog1_argv)<0)
            printf("exec failed");
    }
    if(p1>0)
    {
        printf("im in parent\n");
        waitpid(p1,NULL,0);
        printf("parent: child exited. Now test the pipe\n");
        close(pipe_fd[1]);
        if(dup2(pipe_fd[0],0)<0)
        {
            printf("dup failed:%d\n",errno);
        }
        close(pipe_fd[0]);

        if(execvp (prog2_argv[0], prog2_argv)<0)
            printf("exec failed");

    }

}
4

2 に答える 2

1

grep の引数をオーバーライドします。試す:

int main()
{
  pid_t p1,p2;
  char *prog1_argv[4];
  char *prog2_argv[3];
  /* Build argument list */
  prog1_argv[0] = "ls";
  prog1_argv[1] = "-l";
  prog1_argv[2] = "/";
  prog1_argv[3] = NULL;
  prog2_argv[0] = "grep";
  prog2_argv[1] = "s";
  prog2_argv[2] = NULL;
  // ...
于 2013-09-30T13:23:25.173 に答える
1

ls基本的に、 を実行する前に が終了するのを待つべきではありませんgrep

lsコマンドは、パイプに格納できないほど大量のデータを生成する可能性があるため、ls他のプロセスがパイプから読み取るまでコマンドはブロックされますが、他のプロセスはls完了するのを待ってからパイプから何かを読み取ろうとします。パイプ。これはデッドロックです。

また、そのように待機することにより、シリアル実行が強制され、複数のコアの利点が失われます。

いくつかの小さな改善点があります。エラーを報告するポイントはさまざまです。エラーは、 ではなく、標準エラー ストリーム ( stderr) で報告する必要がありますstdout。また、これらのエラーの少なくともいくつかの後にプログラムが続行されないようにする必要があります。

exec*()システム コールからの戻り値をテストする必要はありません。関数が返された場合、関数は失敗しています。繰り返しますが、その後プロセスが終了することを確認する必要があります。このプログラムでは、子供が続けるかどうかは問題ではありません。多くのプログラムでは、終了しないと混乱が生じます (たとえば、2 つのプロセスが同時に標準入力を読み込もうとするなど)。

pipe_fdグローバル変数である必要はありません。すべてのメッセージが改行で終わっていることを確認してください。を含めなかった<sys/wait.h>ので、関数のスコープ内のプロトタイプなしで作業していましたwaitpid()— これは一般的に悪い考えです。コンパイラを fussy に設定して、すべての関数が使用または定義される前にスコープ内にプロトタイプがあることを要求する必要があります。定義で引数リストを初期化できます。

char *prog1_argv[] = { "ls", "-l", "/", NULL };
char *prog2_argv[] = { "grep", "s", NULL };

prog_argv2[1]これには、NULL ポインターでザッピングしないという非常に有益な副作用があります ( Matthias回答で指摘されているように、配列のサイズも削除しました。2 番目のサイズは 2 で、3 にする必要がありましたが、初期化するとこのように、コンパイラはカウントを行います。

正しく実行するために重要なことの 1 つは、パイプ ファイル記述子がすべて閉じられていることを確認することです。

これは私にとって正しく機能します:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main(void)
{
    pid_t p1;
    int pipe_fd[2];
    char *prog1_argv[] = { "ls", "-l", "/", NULL };
    char *prog2_argv[] = { "grep", "s", 0 };
    if (pipe(pipe_fd) < 0)
    {
        fprintf(stderr, "pipe failed:%d\n", errno);
        exit(1);
    }
    p1 = fork();
    if (p1 == 0)
    {
        printf("In child\n");
        close(pipe_fd[0]);
        if (dup2(pipe_fd[1], 1) < 0)
        {
            fprintf(stderr, "dup failed:%d\n", errno);
            exit(1);
        }
        close(pipe_fd[1]);
        execvp(prog1_argv[0], prog1_argv);
        fprintf(stderr, "exec failed:%d\n", errno);
        exit(1);
    }
    if (p1 > 0)
    {
        printf("In parent\n");
        close(pipe_fd[1]);
        if (dup2(pipe_fd[0], 0) < 0)
        {
            fprintf(stderr, "dup failed:%d\n", errno);
            exit(1);
        }
        close(pipe_fd[0]);

        execvp(prog2_argv[0], prog2_argv);
        fprintf(stderr, "exec failed:%d\n", errno);
        exit(1);
    }
    fprintf(stderr, "Fork failed:%d\n", errno);
    return(1);
}
于 2013-09-30T13:23:30.520 に答える