是什么让 Unix 进程因管道损坏而死亡?

sia*_*mii 32 pipe

以下是我想到的一些选项,不确定哪个是正确的。

  1. 从管道读取 I/O 错误。
  2. 写入管道另一端的进程因失败而终止。
  3. 所有可以写入管道的进程都已关闭它。
  4. 管道的写缓冲区已满。
  5. 对等方已关闭双工管道的另一个方向。
  6. 写入失败,因为没有可以从管道读取的进程。
  7. 系统调用返回了 EPIPE 错误,并且没有安装错误处理程序。

Sté*_*las 45

当进程尝试写入没有读取器的 SOCK_STREAM 类型的管道(已命名或未命名)或套接字时,它会收到一个 SIGPIPE。

这通常是通缉的行为。一个典型的例子是:

find . | head -n 1
Run Code Online (Sandbox Code Playgroud)

您不想findhead终止后继续运行(然后关闭在该管道上打开以供读取的唯一文件描述符)。

yes命令通常依赖于该信号来终止。

yes | some-command
Run Code Online (Sandbox Code Playgroud)

将写“y”直到某个命令终止。

请注意,这不仅是在命令退出时,而是在所有读取器都已关闭对管道的读取 fd 时。在:

yes | ( sleep 1; exec <&-; ps -fC yes)
      1 2       1        0
Run Code Online (Sandbox Code Playgroud)

将有 1 个(子外壳),然后是 2(子外壳 + 睡眠),然后是 1(子外壳),然后是在子外壳明确关闭其标准输入后从管道读取的 0 fd,这就是什么时候yes会收到一个 SIGPIPE。

上面,大多数 shell 使用pipe(2)whileksh93使用 a socketpair(2),但在这方面的行为是相同的。

当进程忽略 SIGPIPE 时,写入系统调用(通常write,但也可能是pwrite, send, splice...)返回EPIPE错误。因此,想要手动处理损坏管道的进程通常会忽略 SIGPIPE 并对 EPIPE 错误采取行动。


gol*_*cks 13

(6)

写入失败,因为没有可以从管道读取的进程。

尽管除非您复制描述符和 fork,否则只能从一个进程开始:通常一个管道有一个读取器和一个写入器,当其中一个关闭连接时,管道就失效了。如果您使用命名管道,您可以使用它建立多个连接(串行),但从这个意义上说,每个连接都代表一个新管道。因此,线程或进程的“管道”与文件描述符同义。

来自man 7 pipe

如果所有引用管道读取端的文件描述符都已关闭,则 write(2) 将导致为调用进程生成 SIGPIPE 信号。如果调用进程忽略此信号,则 write(2) 失败并显示错误 EPIPE。

所以“断管”对作者来说就像 EOF 对读者一样。


Sid*_*idJ 0

当读取进程在写入进程之前退出时,就会发生管道损坏。所以我会选择(6)

  • 可以有多个进程对管道进行读取或写入操作,并且同一个进程可以同时进行读取和写入操作。另外,这不是退出,而是关闭文件描述符。 (2认同)