*nix 系的一个管道问题

2017-06-01 23:26:18 +08:00
 geelaw

Codepad 链接: http://codepad.org/LFkfcDXW

起因是我发现 Windows Command Prompt 的管道的一个有趣行为:

考虑命令 a | b,如果 b 结束之后 a 不退出(比如 ayes,无脑循环输出 y 加换行),则 cmd 会 hang,可以用 Ctrl+C 打断

这个问题自然是可以解决的,只要实现 yes 的时候判断一下输出有没有成功即可。

自然我想知道 *nix 系里的管道是怎么一个情况。我的一个朋友告诉我执行 yes | 另一个程序 的时候,另一个程序结束的时候 yes 也会结束,不会 hang 住终端,也不会需要自己去 kill 那个 yes。我对这个机制感到很好奇……想问一下,如果有下面两段代码:

/* program1.c */
#include<cstdio>
#include<unistd.h>
int main(void)
{
sleep(5);
fputs("I wrote to stderr!\n", stderr);
return 0;
}

/* program2.c */
int main(void){return 0;}

如果我编译、执行 program1 | program2,那么有如下可能:

  1. 立刻结束,不会看到 I wrote to stderr!
  2. 立刻结束,但是五秒之后会突然蹦出来 I wrote to stderr!
  3. 不立刻结束,5 秒之后出现 I wrote to stderr! 并结束。

如果是情况 1,显然是终端在管道末尾的程序停止的时候就 kill 了前面的所有进程;如果是情况 2,则是终端在管道末尾的程序挺值得时候就继续给出 prompt,但是前面的程序并没有停止;如果是情况 3,那就是终端要等到管道里面每个程序都停止的时候才继续给出 prompt。

请问是哪一种呢?如果您有闲、使用 *nix 并想要帮助我,请帮我测试一下,谢谢 :-)

1859 次点击
所在节点    问与答
9 条回复
privil
2017-06-01 23:38:28 +08:00
……我测试了,不会解释,五秒钟后会继续,不过我是随手用 shell 写的程序
privil
2017-06-01 23:38:55 +08:00
@privil 情况 3
pagxir
2017-06-01 23:45:02 +08:00
还是多系统的看书吧。yes 被自动结束是因为,它往一个没有读者的管道写数据而触发 broken pipe。broken pipe 的缺省动作是让程序终止
pagxir
2017-06-01 23:46:57 +08:00
还是 windows 的 ctrl c 是假的,并不能真的触发 sigint。即 window 不支持作业控制。
geelaw
2017-06-02 00:01:25 +08:00
@pagxir (自动解析为“多看系统的书”)很感谢您提供的原因。

问这个问题是因为 Windows 上的行为并不是这样啊;而且我不是 *nix 用户,所以才来问。

`WriteFile` 并不一定要让程序崩溃,只要输入的参数(在这个例子里面,传入的 handle 没有被关掉,直到这个 handle 被关掉才可以崩溃)还是有效的——它返回一个错误代码。而 `putchar` 的标准也只是说,如果成功则返回等于输入的值,并不一定要崩溃。

Windows 的控制台 Ctrl+C 是可控制的,不是假的,根据这篇 MSDN 文档: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682541(v=vs.85).aspx

一个常见的错误是把“系统”解读为“*nix ”。
geelaw
2017-06-02 00:10:06 +08:00
--- CASE CLOSED ---

*nix `yes` 会自己结束的原因是向无读者管道输出,而不是单纯“因为后一个程序已经结束”,也不是被终端、命令解释器杀死的。

后一个程序结束之后前一个程序不一定要结束,并且终端、命令解释器会等到管道里面所有的程序都结束才会继续给出 prompt。

--- CASE CLOSED ---

我写的 PowerShell 二进制管道的实现里面,只要最后一个程序结束,对管道的消费就会结束,prompt 就可以出现了;我在思考是否应该等待管道里面所有的程序都结束再结束对管道的消费。
billlee
2017-06-02 01:25:34 +08:00
Shell 是不会杀你的进程的,终端更不会杀进程。
POSIX 下,write 对端已经关闭的 pipe 会产生 SIGPIPE, 这个信号的默认行为是结束进程。你这个程序里面并没有向标准输出写数据,也就不会产生 SIGPIPE, 所以是情况 3.
写 TCP 连接同样会有这样的问题,所以网络程序一般都是要忽略 SIGPIPE 的。
xss
2017-06-02 09:53:32 +08:00
如果你执行的是
./a | ./b
结果是等待 5 秒, 然后输出, 即情况 3
如果你执行的是
./a 2>&1 | ./b
结果是立刻结束, 无输出, 即情况 1

你自己结合楼上的说的自己体会吧......
xss
2017-06-02 09:54:40 +08:00
错了, 第二种情况是等待 5s, 无输出, 不是立刻结束....

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/365340

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX