【问题标题】:Unable to write into pipe IPC for 2 processes无法写入 2 个进程的管道 IPC
【发布时间】:2013-09-30 13:12:11
【问题描述】:

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

    }

}

【问题讨论】:

    标签: c ipc pipe


    【解决方案1】:

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

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

    此外,通过这样的等待,您会强制执行串行执行,这会失去多核的好处。

    您应该进行一些小的改进。您可以在多个点报告错误。应在标准错误流 (stderr) 上报告错误,而不是在 stdout 上报告。您还应该确保程序至少在其中一些错误之后不会继续运行。

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

    pipe_fd 没有必要成为全局变量。请确保您的所有消息都以换行符结尾。您没有包含&lt;sys/wait.h&gt;,因此您在没有waitpid() 函数范围内的原型的情况下工作——这通常是个坏主意。您应该将编译器设置为 fussy,因此它要求每个函数在使用或定义之前在范围内都有一个原型。您可以在定义中初始化参数列表:

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

    【讨论】:

    • 你好乔纳森。使用此代码,我想检查第一个进程是否要激活,以便第二个进程从管道中读取。或者,第一个进程是否有可能写入第二个进程的“读取端”并死掉。稍后第二个进程有可能从管道中读取(即使没有进程 1 处于活动状态)
    • 我只是了解管道在 linux 中的工作原理。例如,当有人发出如下的 shell 脚本时 'ls -l |更多',我想知道内部发生了什么。 ls 在执行 more 之前是否已完全执行?
    • 对评论1的回答:您不需要进行任何此类检查,尝试这样做通常会适得其反。第一个进程 (ls) 写入管道的写入端,因此数据可用于管道读取端的第二个进程 (grep)。如果管道被填满(容量不同,但只能是 5-8 KiB),那么第一个进程将阻塞,直到管道被清空(因此我提到了死锁)。管道可以传送兆字节的数据,因此两个进程必须同时运行。如果ls 在写入管道后死掉了,数据仍然在那里供grep 读取。
    • 对评论 2 的回答:正如我对评论 1 的回答(以及主要答案)中所指出的,管道两端的进程同时运行。这很好。想象一个find / -print | grep s | more 命令。可能有许多兆字节的数据要显示,但每个管道的容量只有几 KiB。这些进程必须同时运行才能有机会工作。如果它们按顺序运行,find 将被阻止等待grep,而grep 将无法启动,因为find 尚未完成。
    • 这回答了我的很多问题!非常感谢乔纳森和马蒂亚斯!!
    【解决方案2】:

    您覆盖了 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;
      // ...
    

    【讨论】:

    • 啊!我错过了明显的!但也有一个小问题。我想将第二个进程的输出显示到终端中。但这并没有发生。我没有将进程('grep')的输出终端更改为其他任何内容。但是输出根本不会到达终端。你知道可能是什么问题吗?
    • 修改后对我来说效果很好。 prog2_argv的大小你也改了吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多