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*()系统调用的返回值。如果函数返回,则失败。同样,您应该确保该过程在此之后退出。在这个程序中,孩子继续没有关系;在许多程序中,不退出会导致混乱(例如,两个进程试图同时读取标准输入)。

没有必要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,需要为 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);
}
于 2013-09-30T13:23:30.520 回答