Codepad 链接: http://codepad.org/LFkfcDXW
起因是我发现 Windows Command Prompt 的管道的一个有趣行为:
考虑命令
a | b
,如果b
结束之后a
不退出(比如a
是yes
,无脑循环输出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
,那么有如下可能:
I wrote to stderr!
;I wrote to stderr!
;I wrote to stderr!
并结束。如果是情况 1,显然是终端在管道末尾的程序停止的时候就 kill 了前面的所有进程;如果是情况 2,则是终端在管道末尾的程序挺值得时候就继续给出 prompt,但是前面的程序并没有停止;如果是情况 3,那就是终端要等到管道里面每个程序都停止的时候才继续给出 prompt。
请问是哪一种呢?如果您有闲、使用 *nix 并想要帮助我,请帮我测试一下,谢谢 :-)
1
privil 2017-06-01 23:38:28 +08:00 1
……我测试了,不会解释,五秒钟后会继续,不过我是随手用 shell 写的程序
|
3
pagxir 2017-06-01 23:45:02 +08:00 via Android 1
还是多系统的看书吧。yes 被自动结束是因为,它往一个没有读者的管道写数据而触发 broken pipe。broken pipe 的缺省动作是让程序终止
|
4
pagxir 2017-06-01 23:46:57 +08:00 via Android
还是 windows 的 ctrl c 是假的,并不能真的触发 sigint。即 window 不支持作业控制。
|
5
geelaw OP @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 ”。 |
6
geelaw OP --- CASE CLOSED ---
*nix `yes` 会自己结束的原因是向无读者管道输出,而不是单纯“因为后一个程序已经结束”,也不是被终端、命令解释器杀死的。 后一个程序结束之后前一个程序不一定要结束,并且终端、命令解释器会等到管道里面所有的程序都结束才会继续给出 prompt。 --- CASE CLOSED --- 我写的 PowerShell 二进制管道的实现里面,只要最后一个程序结束,对管道的消费就会结束,prompt 就可以出现了;我在思考是否应该等待管道里面所有的程序都结束再结束对管道的消费。 |
7
billlee 2017-06-02 01:25:34 +08:00 2
Shell 是不会杀你的进程的,终端更不会杀进程。
POSIX 下,write 对端已经关闭的 pipe 会产生 SIGPIPE, 这个信号的默认行为是结束进程。你这个程序里面并没有向标准输出写数据,也就不会产生 SIGPIPE, 所以是情况 3. 写 TCP 连接同样会有这样的问题,所以网络程序一般都是要忽略 SIGPIPE 的。 |
8
xss 2017-06-02 09:53:32 +08:00
如果你执行的是
./a | ./b 结果是等待 5 秒, 然后输出, 即情况 3 如果你执行的是 ./a 2>&1 | ./b 结果是立刻结束, 无输出, 即情况 1 你自己结合楼上的说的自己体会吧...... |
9
xss 2017-06-02 09:54:40 +08:00
错了, 第二种情况是等待 5s, 无输出, 不是立刻结束....
|