调用popen后如何在父子进程之间使用管道?

时间:2019-02-03 17:46:21

标签: c pipe parent-child ipc interprocess

我想与以下子进程进行通信:

int main(int argc, char *argv[])
{
    int bak, temp;
    int fd[2];
    if (pipe(fd) < 0)
    {
        // pipe error
        exit(1);
    }
    close(fd[0]);
    dup2(STDOUT_FILENO, fd[1]);
    fflush(stdout);
    bak = dup(1);
    temp = open("/dev/null", O_WRONLY);
    dup2(temp, 1);
    close(temp  );
    Mat frame;
    std::vector<uchar> buf;
    namedWindow( "Camera", WINDOW_AUTOSIZE );
    VideoCapture cam(0 + CAP_V4L);
    sleep(1);
    if (!cam.isOpened())
    {
        cout << "\nCould not open reference " << 0 << endl;
        return -1;
    }
    for (int i=0; i<30; i++)
    {
        cam>>frame;
    }
    //cout<<"\nCamera initialized\n";
    /*Set the normal STDOUT back*/
    fflush(stdout);
    dup2(bak, 1);
    close(bak);

    imencode(".png",frame, buf);
    cout<<buf.size()<<endl;
    ssize_t written= 0;
    size_t s = 128;
    while (written<buf.size())
    {
     written += write(fd[1], buf.size()+written, s);

    }
    cout<<'\0';
    return 0;

}

父级使用popen调用与上述源代码编译相对应的过程。

请注意,我正在写与pipe重复的标准输出。

父级将读取数据并将其重新发送到UDP套接字。

如果我做这样的事情:

#define BUFLEN 128
FILE *fp;
char buf[BUFLEN];
if ((fp = popen("path/to/exec", "r")) != NULL)
{
    while((fgets(buf, BUFLEN, fp)!=NULL))
    {
        sendto(sockfd, buf, strlen(buf),0, addr, alen);
    }
}

程序正在运行,即sendto的接收器将接收数据。

我尝试在子进程中使用pipe

    int fd[2];
    if (pipe(fd) < 0)
    {
        // pipe error
        exit(1);
    }
    close(fd[1]);
    dup2(STDIN_FILENO, fd[0]);
    if ((fp = popen("path/to/exec", "r")) != NULL)
    {
        while((read(fd[0], buf, BUFLEN) > 0)
        {
            sendto(sockfd, buf, strlen(buf),0, addr, alen);
        }
    }

但是不发送。

那么在这种情况下如何使用pipe来实现与第一种情况相同的行为?我应该dup2(STDIN_FILENO, fd[0]);还是dup2(STDOUT_FILENO, fd[0]);

我正在使用sandard,因为文件描述符是由子进程继承的,因此不需要任何其他工作。这就是为什么我认为我可以使用pipe的原因,

2 个答案:

答案 0 :(得分:2)

在父母中:

if (pipe(fd) < 0)
{
    // pipe error
    exit(1);
}
close(fd[0]);

您得到一个管道,然后立即将其一端关闭。该管道现在无用,因为没有人能够恢复封闭端,因此没有数据可以流过封闭端。您已将管道转换为一端密封的空心圆柱体。

然后在孩子中:

if (pipe(fd) < 0)
{
    // pipe error
    exit(1);
}
close(fd[1]);

您创建另一个 unrelated 管道,并将其另一端密封。这两个管道没有连接,现在您有两个单独的空心自行车,每个自行车的一端都密封。任何东西都不能流过。

如果在第一个圆柱体中放东西使其在另一个圆柱体中出现,那将是一个非常不错的魔术。解决方案是在没有把手或镜面巧妙布置的情况下,创建一个管道,使两端保持打开状态,然后将数据插入。 >

答案 1 :(得分:1)

手动设置管道以使父进程可以从其读取子进程的标准输出的常用方法包括以下常规步骤:

  • 父级通过调用pipe()
  • 创建管道
  • 父母fork()
  • 父级关闭(说明:其副本)管道的写入端
  • child通过dup2()将管道的写入端复制到其标准输出上
  • child关闭管道写入端的原始文件描述符
  • (可选)子项关闭管道的读取端(说明:其副本
  • 子执行所需的命令,或者直接执行所需的工作

然后,父级可以从管道的读取端读取子级的输出。

popen()函数为您完成所有这些操作,并将父级的管道末端包裹在FILE中。当然,如果调用者要求的话,它可以并且将建立一个朝相反方向的管道。

您需要了解和理解,在上述程序方案中,重要的是哪个动作由哪个流程执行,以及相对于同一流程中的其他动作以什么顺序执行。特别是,父级不得在启动子级之前关闭管道的写入端,因为这会使管道失效。子级继承了一端封闭的管道,无法通过该管道传输任何数据。

对于后一个示例,还请注意,对于父级或子级,将标准输入重定向到管道的读取端不属于该过程。您的管道是半封闭的,因此无论如何都无法读取,这一事实只是锦上添花。而且,父级以这种方式修饰自己的标准输入。不一定是错误的,但是父母甚至不依赖它。

总的来说,您似乎不会欣赏到更大的画面。即使您执行了您似乎希望在父级中进行的重定向,以便它可以被子级继承, popen()也会执行自己的重定向到管道自己的创建。它返回的FILE *是读取孩子的输出的方法。您先前可能没有执行过任何输出重定向(澄清:孩子的标准输出)。

原则上,可以使用与您类似的方法来创建另一个重定向,但是此时popen()的便利性完全丧失了。如果您想重定向孩子的输入pipe / fork / dup2 / exec直通路线。 em>输出。


将所有内容应用于第一个示例,您必须了解,尽管一个进程可以重定向其自己的标准流,但无法以这种方式建立到其父进程的管道。 父级需要提供管道,否则它不知道。当一个进程将一个文件描述符复制到另一个文件描述符时,它将用新的文件描述符替换该文件描述符,并在打开时关闭该文件描述符。它不会重新定义原始文件。当然,在这种情况下,一旦两端不再在任何地方打开,则管道也就无用了。