以下是我想到的一些选择,不确定哪一个是正确的。
- 从管道读取 I/O 错误。
- 写入管道另一端的进程因失败而终止。
- 所有可以写入管道的进程都已关闭它。
- 管道的写入缓冲区已满。
- 对等方已关闭双工管道的另一个方向。
- 写入失败,因为没有进程可以从管道读取。
- 系统调用返回 EPIPE 错误,并且没有安装错误处理程序。
答案1
当进程尝试写入没有剩余读取器的 SOCK_STREAM 类型的管道(命名或未命名)或套接字时,进程会收到 SIGPIPE。
这通常是想要的行为。一个典型的例子是:
find . | head -n 1
您不想在终止find
后继续运行head
(然后关闭该管道上唯一打开用于读取的文件描述符)。
该yes
命令通常依赖于该信号来终止。
yes | some-command
将写入“y”直到某个命令终止。
请注意,这不仅是在命令退出时,也是在所有读取器关闭对管道的读取 fd 时。在:
yes | ( sleep 1; exec <&-; ps -fC yes)
1 2 1 0
在子 shell 显式关闭其 stdin 后,将有 1(子 shell)、2(子 shell + sleep)、1(子 shell)、0 fd 从管道读取,此时yes
将收到 SIGPIPE。
上面,大多数 shell 使用pipe(2)
whileksh93
使用 a socketpair(2)
,但在这方面的行为是相同的。
当进程忽略 SIGPIPE 时,写入系统调用(通常是write
,但也可能是pwrite
, send
, splice
...)会返回错误EPIPE
。因此,想要手动处理损坏的管道的进程通常会忽略 SIGPIPE 并根据 EPIPE 错误采取行动。
答案2
(6)
写入失败,因为没有进程可以从管道读取。
尽管除非您复制描述符并分叉,否则只能有一个进程开始:通常一个管道有一个读取器和一个写入器,当其中一个关闭连接时,管道将失效。如果您使用命名管道,则可以与其建立多个连接(串行),但每个连接在这个意义上都代表一个新管道。因此,线程或进程的“管道”与文件描述符同义。
从man 7 pipe
:
如果引用管道读取端的所有文件描述符都已关闭,则 write(2) 将导致为调用进程生成 SIGPIPE 信号。如果调用进程忽略此信号,则 write(2) 会失败并出现错误 EPIPE。
因此,“断管”对于作者来说就像 EOF 对于读者一样。
答案3
当读取进程在写入进程之前退出时,就会发生管道损坏。所以我会选择(6)