Linux管道作为输入和输出

3
我希望在Linux操作系统中的C程序中执行以下操作:
  • 使用系统调用(或2)创建PIPE
  • 使用exec()执行新进程
  • 将进程的STDIN连接到先前创建的管道。
  • 将进程的输出连接到另一个PIPE。

这样可以绕过任何驱动器访问以提高性能。

我知道使用PIPE系统调用创建管道非常简单,而且我只需使用popen为输入或输出目的创建管道即可。

但是,如何同时为输入和输出创建管道呢?

3个回答

7

在处理管道时需要非常小心:

  1. 调用pipe()两次,一次用于产生从父进程到子进程的管道,另一次用于产生从子进程到父进程的管道,共产生4个文件描述符。
  2. 调用fork()。
  3. 在子进程中:
    • 关闭标准输入(文件描述符0)。
    • 调用dup()或dup2()将管道的读端变成标准输入。
    • 关闭管道的读端。
    • 关闭管道的写端。
    • 关闭标准输出(文件描述符1)。
    • 调用dup()或dup2()将管道的写端变成标准输出。
    • 关闭管道的写端。
    • 关闭管道的读端。
    • 执行所需的程序。
  4. 在父进程中:
    • 关闭从父进程到子进程的管道的读端。
    • 关闭从子进程到父进程的管道的写端。
    • 循环将数据发送到子进程的管道写端并从子进程的管道读端读取数据。
    • 当不再向子进程发送数据时,关闭从父进程到子进程的管道的写端。
    • 当接收到所有数据时,关闭从子进程到父进程的管道的读端。

请注意关闭的次数,特别是在子进程中。如果使用dup2(),则不必显式地关闭标准输入和输出;但是,如果进行了显式关闭,则dup()将正常工作。还要注意,无论是dup()还是dup2()都不会关闭被复制的文件描述符。如果省略关闭管道,则两个程序都无法正确检测EOF;当前进程仍然可以向管道写入数据,这意味着管道上没有EOF,程序将无限期挂起。

请注意,此解决方案不会更改子进程的标准错误输出;它与父进程的标准错误输出相同。通常情况下,这是正确的。如果您有特定的要求,需要以不同方式处理子进程的错误消息,请对子进程的标准错误输出采取适当的措施。


感谢您的回答。 理论上,解决方案会生成两个额外的进程。 一个是子进程,另一个是外部二进制本身。 在这种情况下,预期的性能损失是多少?是否存在“单进程”解决方案?使用这样的解决方案是否会有所收获? - Neville Bamshoe
exec()函数替换了分叉出的子进程。另一种避免使用exec()的方法是将子代码包含在父进程可执行文件中。相同的管道规则适用-只需将最后的'exec'系统调用替换为对处理函数的调用即可。 - Jonathan Leffler

1

根据您的需求,您可能会发现使用mkfifo(1)来创建命名管道并让您的进程读/写该文件更容易。虽然该文件在文件系统中被命名,但使用匿名管道的开销不应该是可感知的。


在这个问题的背景下,必须有两个单独的FIFO,一个用于与子进程通信,另一个用于从子进程通信。您还需要担心程序的并发执行 - 使用了哪些名称 - 并且您还需要担心在之后(或程序崩溃时)清理FIFO。嗯,FIFO有什么优点呢? - Jonathan Leffler
这就是为什么我说取决于需求。根据 OP 的情况,可能根本不需要编写任何代码,特别是因为他说他想执行(3)。 - Suppressingfire

0

最简单的方法可能是执行/bin/sh,这样你可以使用熟悉的Shell语法来指定管道,并分担所有复杂性。

类似这样:

execlp("sh", "-c", "cat /dev/zero | cat > /dev/null", NULL);

在某些情况下,这可能会起作用 - 使用不同的管道而不是上面那个只消耗CPU的管道。然而,问题描述了一个写入子进程并从子进程获取响应的过程;在您的示例中,第二个cat必须将其输出反馈给第一个cat。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接