无法写入2个进程的管道IPC

时间:2013-09-30 13:12:12

标签: c ipc pipe

我有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");

    }

}

2 个答案:

答案 0 :(得分: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;
  // ...

答案 1 :(得分:1)

从根本上说,在运行ls之前,您不应该等待grep死亡。

ls命令可能会生成如此多的数据,导致它们无法全部存储在管道中,因此ls命令将阻塞,直到另一个进程从管道读取,但另一个进程在尝试从管道读取任何内容之前等待ls完成。这是一个僵局。

此外,通过这样等待,您可以强制执行串行执行,从而避免了多核的好处。

您应该做一些小的改进。报告错误有多种方面。应在标准错误流(stderr)上报告错误,而不是stdout。您还应该确保在至少一些错误之后程序不会继续。

您不必测试任何exec*()系统调用的返回值。如果函数返回,则失败。而且,您应该确保在此之后退出流程。在这个计划中,孩子继续并不重要;在许多程序中,不退出会导致混乱(例如,两个进程同时尝试读取标准输入)。

pipe_fd不需要成为全局变量。请确保您的所有邮件以换行符结尾。您没有包含<sys/wait.h>,因此您在waitpid()函数的范围内没有原型工作 - 这通常是一个坏主意。您应该将编译器设置为繁琐,因此它要求每个函数在使用或定义之前都在范围内具有原型。您可以在定义中初始化参数列表:

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

这有一个至关重要的有益副作用,即不用{NULL}指针prog_argv2[1]切换Matthias answer。我还删除了数组的大小;第二个的大小为2,需要为3,但是当你像这样初始化时,编译器会进行计数。

你正确做的一件事是正确的做法是确保管道文件描述符全部关闭。

这适用于我:

#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);
}