Answers:
我不买以前接受的答案。SIGPIPE
确切地是在write
失败时生成的EPIPE
,而不是事先生成的-实际上,避免SIGPIPE
不更改全局信号配置的一种安全方法是使用临时屏蔽它pthread_sigmask
,执行write
,然后执行sigtimedwait
(超时为零)以消耗任何待处理的SIGPIPE
信号(发送到调用线程,而不是进程),然后再次对其进行屏蔽。
我相信SIGPIPE
存在的原因要简单得多:为纯“过滤器”程序建立合理的默认行为,该程序可以连续读取输入,以某种方式对其进行转换并写入输出。如果没有SIGPIPE
,则除非这些程序显式处理写错误并立即退出(总之,这不是所有写错误的理想行为),否则它们将继续运行,直到它们的输入用尽,即使它们的输出管道已关闭。当然,您可以SIGPIPE
通过显式检查EPIPE
并退出来复制行为,但是的全部目的SIGPIPE
是在程序员懒惰时默认实现此行为。
因为您的程序可能正在等待I / O或被挂起。SIGPIPE异步中断程序,终止系统调用,因此可以立即处理。
更新资料
考虑一条管道A | B | C
。
仅出于确定性考虑,我们假定B是规范的复制循环:
while((sz = read(STDIN,bufr,BUFSIZE))>=0)
write(STDOUT,bufr,sz);
B
在read(2)调用中被阻止,以等待终止A
时的数据C
。如果您等待write(2)的返回代码,B何时会看到它?答案当然不是直到A写更多的数据(这可能是一个漫长的等待-如果A被其他东西阻塞了怎么办?)。顺便说一下,请注意,这也使我们可以使用一个更简单,更简洁的程序。如果您依赖于写入的错误代码,则需要类似以下内容:
while((sz = read(STDIN,bufr,BUFSIZE))>=0)
if(write(STDOUT,bufr,sz)<0)
break;
另一个更新
啊哈,您对写入行为感到困惑。您会看到,当关闭带有挂起写操作的文件描述符时,SIGPIPE就在此时发生。尽管写入最终将返回-1 ,但信号的全部目的是异步通知您不再可能进行写入。这是使管道的整个优雅的协同例程结构在UNIX中工作的一部分。
现在,我可以在几本UNIX系统编程书籍中为您提供整个讨论,但是有一个更好的答案:您可以自己验证。编写一个简单的B
程序[1]-您已经有了勇气,您所需要的只是一个main
,其中有些包含-并为添加一个信号处理程序SIGPIPE
。运行类似的管道
cat | B | more
然后在另一个终端窗口中,将调试器连接到B,然后在B信号处理程序中放置一个断点。
现在,杀死更多,B应该在信号处理程序中中断。检查堆栈。您会发现读取仍在进行中。让信号处理程序继续执行并返回,然后查看write返回的结果- 然后为-1。
[1]自然,您将用C编写B程序。
SIGPIPE
是不是在读期间发表,但在write
。你并不需要写一个C程序进行测试,只需运行cat | head
,并pkill head
在一个单独的终端。您将看到cat
快乐地等待它的出现- read()
仅当您键入内容并按Enter时cat
,它才会因管道破裂而死,正是因为它试图写入输出。
SIGPIPE
不能传送到B
while B
被阻止,read
因为SIGPIPE
只有在B
尝试时才会生成write
。同时调用write
时,没有线程可以“正在等待I / O或挂起” 。
SIGPIPE
被阻止时正在上升read
吗?我根本无法重现这种行为(并且实际上不确定为什么我首先要接受这种行为)
https://www.gnu.org/software/libc/manual/html_mono/libc.html
该链接说:
必须同时在两端打开管道或FIFO。如果您从没有任何进程写入的管道或FIFO文件中读取(可能是因为它们已全部关闭文件或退出了),则读取将返回文件末尾。写入没有读取过程的管道或FIFO被视为错误条件;它会生成SIGPIPE信号,如果该信号被处理或阻塞,则会失败并显示错误代码EPIPE。
—宏:int SIGPIPE
管道破损。如果使用管道或FIFO,则必须设计应用程序,以便一个进程打开管道进行读取,然后另一个进程开始写入。如果读取过程从未开始或意外终止,则写入管道或FIFO会引发SIGPIPE信号。如果SIGPIPE被阻止,处理或忽略,则有问题的调用将失败,而是使用EPIPE。
管道和FIFO特殊文件在管道和FIFO中有更详细的讨论。
机器信息:
Linux 3.2.0-53-generic#81-Ubuntu SMP Thu Aug 22 21:01:03 UTC 2013 x86_64 x86_64 x86_64 GNU / Linux
gcc版本4.6.3(Ubuntu / Linaro 4.6.3-1ubuntu5)
我在下面编写了这段代码:
// Writes characters to stdout in an infinite loop, also counts
// the number of characters generated and prints them in sighandler
// writestdout.c
# include <unistd.h>
# include <stdio.h>
# include <signal.h>
# include <string.h>
int writeCount = 0;
void sighandler(int sig) {
char buf1[30] ;
sprintf(buf1,"signal %d writeCount %d\n", sig, writeCount);
ssize_t leng = strlen(buf1);
write(2, buf1, leng);
_exit(1);
}
int main() {
int i = 0;
char buf[2] = "a";
struct sigaction ss;
ss.sa_handler = sighandler;
sigaction(13, &ss, NULL);
while(1) {
/* if (writeCount == 4) {
write(2, "4th char\n", 10);
} */
ssize_t c = write(1, buf, 1);
writeCount++;
}
}
// Reads only 3 characters from stdin and exits
// readstdin.c
# include <unistd.h>
# include <stdio.h>
int main() {
ssize_t n ;
char a[5];
n = read(0, a, 3);
printf("read %zd bytes\n", n);
return(0);
}
输出:
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 11486
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 429
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 281
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 490
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 433
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 318
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 468
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 11866
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 496
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 284
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 271
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 416
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 11268
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 427
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 8812
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 394
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 10937
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 10931
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 3554
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 499
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 283
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 11133
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 451
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 493
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 233
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 11397
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 492
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 547
$ ./writestdout | ./readstdin
read 3 bytes
signal 13 writeCount 441
您可以看到,在每种情况下SIGPIPE
,只有在写入过程写入了3个以上的字符后,才会收到该字符。
这是否不能证明SIGPIPE
在读取过程终止后立即尝试将更多数据写入封闭的管道后不会立即生成?
write
。