【问题标题】:Confusion with dup2(), exec() and pipes与 dup2()、exec() 和管道混淆
【发布时间】:2015-07-03 17:16:54
【问题描述】:

我一直在努力理解涉及命令dup2()exec() 和管道的概念。

我想要达到的目标:

将程序 X 的输出连接到程序 Y 的输入。

who | sort 之类的基本内容

有一个父级和 2 个子级,其中子级负责执行程序,而父级将程序传递给子级。

这是我对管道的不理解:

P1) 管道被视为文件,应该是单向的,但是是什么阻止我使用一个管道进行多个单向通信通道? 所以,假设我有pipe1 和三个进程(P - 父级 - C1C2、子级)通过分叉打开管道。所有这些进程都可以使用文件描述符。 假设我们一切正常,关闭未使用的管道末端,P 现在向C1 写入一些内容。 再次使用管道在C1C2 之间进行通信有什么问题? 就在写这个问题的时候,我突然想到一个想法:谁读取它有问题,而许多进程可能同时打开它(两个进程阻塞读取),即系统无法确定谁想读取写入其中的缓冲数据?如果是这样,这是如何在系统中实现的?

我真的很想理解这个概念,所以请多多包涵。

为了将这个问题应用到现实生活中,我正在处理一些伪代码:

亲:

  • P 关闭 pipe1 的不需要的读取端
  • P 通过pipe1 将程序参数('who')发送到C1
  • P关闭写端
  • P 等待孩子退出

C1:

  • C1pipe1 的读取端读取参数
  • C1dup2()s 标准输出到pipe1 的写入端
  • C1 关闭 pipe1 的两端(因为我们已经欺骗了它)
  • C1execvp()s 程序(“谁”)

C2:

  • C2 dup2()s 将 pipe1 的结尾读取到 stdin 以便它获取将要执行的程序的输入
  • C2 关闭 pipe1 的两端
  • C2 等待来自duped pipe1C1stdin 上的输入
  • C2execvp()s 程序('排序')与此输入


现在,如果我按照上面描述的那样做,我就没有运气了。 但是,如果我引入另一个管道pipe2,它看起来像这样:
亲:
  • P 关闭不需要的管道的两端 pipe2
  • P 关闭 pipe1 的不需要的读取端
  • P 通过pipe1 将程序参数('who')发送到C1
  • P关闭写端
  • P 等待孩子退出

C1:

  • C1 关闭 pipe2 的读取结束
  • C1pipe1 的读取端读取参数
  • C1dup2()s 标准输出到pipe2 的写端
  • C1 关闭 pipe2 的写结束
  • C1 关闭了 pipe1 的两端——在这个孩子中 pipe2pipe1 是多余的
  • C1execvp()s 程序('谁')

C2:

  • C2 dup2()s 读取 pipe2 的结尾到 stdin
  • C2 关闭 pipe1 的两端
  • C2 等待来自duped pipe2C1stdin 上的输入
  • C2 使用此输入执行程序 sort

不应该在多个进程中重用管道的假设是否正确,因为系统可能不确定要“服务”谁?或者还有其他原因吗?

【问题讨论】:

  • 你能详细说明一下吗?我是否正确理解这种情况:P 以某种方式接收到a | b 之类的输入,然后创建 C1 和 C2,C1 执行 a,C2 执行 b。 a 的标准输出应该是 b 的标准输入。我对么?这是一个要求吗? P不能只分叉C1,C1可以分叉C2吗?
  • 另外,execfamily 在指定操作后不会继续,所以 C1 和 C2 将在命令后终止。这意味着每当 P 收到像 a | b 这样的新输入时,它都必须再次分叉。既然如此,P 和 C1 以及 P 和 C2 之间的通信在这里似乎是多余的。
  • @holgac 是的,你是完全正确的。这是一个要求。我会知道如何按照你所说的方式把它拉下来。
  • 是的,我知道exec() 替换了当前进程。家长需要将程序传达给孩子,这是一项要求。
  • 那么正如 Gilles 在他/她的回答中所说,你不能那样做。单向管道意味着读/写端使用相同的缓冲区。相反,您需要在 P 和 C1 之间有一个管道 (p1),在 P 和 C2 之间有另一个管道 (p2),在 C1 和 C2 之间有另一个 (p3)。由于 C1 和 C2 是兄弟姐妹,因此它们无法创建每个人都可以共享的管道。您需要在 C1 之前创建 p1,在 C2 之前创建 p2,在 C1 和 C2 之前创建 p3。

标签: c unix pipe exec dup2


【解决方案1】:

管道主要是为一对一的通信而设计的——一个作者,一个读者。虽然没有什么可以阻止您拥有尽可能多的阅读器和作者,但这种行为通常会使它不太有用,尤其是对于多个阅读器:

  • 从管道读取是一种破坏性操作:通过管道发送的每个字节都将被恰好一个读取器读取,无论谁先抓取它。如果要广播某些信息,则需要使用不同的 IPC 机制或显式复制数据(如tee 命令)。
  • 如果有多个写入者,他们的写入会以某种不可预测的方式穿插。唯一的保证是大小为PIPE_BUF 或更小的写入是原子的。
  • 如果写入器的数量降至零,读取器会看到文件结束情况。

在您描述的架构中,您有两个独立的通信通道:P 将who 发送到 C1,C1 将运行 who 命令的输出发送到 C2。在 shell 脚本中,这将类似于

echo who | { read command; exec command; } | sort

echo who 在原始进程中而不是在子shell 中执行。

您的第一个建议不起作用,因为没有办法说 P 的输出将进入 C1,而 C1 的输出将进入 C2。它仍然是同一个管道,所以 P 的输出可以到 C2 并且 C1 的输出可以返回到它自己,或者它可以是混合的。

【讨论】:

  • 好的,谢谢,所以你基本上证实了我到目前为止的假设。出于上述原因,知道管道用于一对一通信绝对是有道理的。
猜你喜欢
  • 2016-02-26
  • 2011-06-26
  • 1970-01-01
  • 2019-02-13
  • 2018-04-29
  • 2015-07-02
  • 2019-11-29
  • 1970-01-01
相关资源
最近更新 更多