为什么SIGPIPE存在?
根据我的理解, SIGPIPE
只能作为write()
的结果发生,它可以(并且)返回-1,并将errno
为EPIPE
…那么为什么我们有额外的信号开销? 每次我用pipe道工作,我都忽略了SIGPIPE
,从来没有感到任何痛苦,我错过了什么?
我不买以前接受的答案。 SIGPIPE
是在write
失败时产生的,而不是事先 – 事实上,一个避免SIGPIPE
而不改变全局信号configuration的安全方法就是用pthread_sigmask
临时屏蔽它,执行write
,然后执行sigtimedwait
(零超时)挂起SIGPIPE
信号(发送给调用线程,而不是进程),然后再次取消屏蔽。
我相信SIGPIPE
存在的原因要简单得多:为纯粹的“filter”程序build立稳健的默认行为,不断读取input,以某种方式进行转换,并写出输出。 如果没有SIGPIPE
,除非这些程序显式地处理写入错误并立即退出(无论如何,这可能不是所有写入错误的期望行为),它们将继续运行,直到输出pipe道已经closures为止。 当然,您可以通过显式检查EPIPE
并退出来复制SIGPIPE
的行为,但是SIGPIPE
的全部目的是在程序员懒时默认实现这种行为。
因为您的程序可能正在等待I / O或以其他方式暂停。 SIGPIPE会asynchronous中断程序,终止系统调用,因此可以立即处理。
更新
考虑一个pipe道A | B | C
A | B | C
A | B | C
为了明确,我们假设B是规范的复制循环:
while((sz = read(STDIN,bufr,BUFSIZE))>=0) write(STDOUT,bufr,sz);
当C
终止时, B
在读(2)呼叫时被阻塞,等待来自A
数据。 如果等待写入(2)的返回码,B什么时候会看到? 答案当然是,直到A写入更多的数据(这可能是一个漫长的等待 – 如果A被别的东西阻塞了,那该怎么办?)。 请注意,这也使我们可以更简单,更清洁的程序。 如果你依赖写入的错误代码,你需要这样的东西:
while((sz = read(STDIN,bufr,BUFSIZE))>=0) if(write(STDOUT,bufr,sz)<0) break;
另一个更新
啊哈,你对写的行为感到困惑。 你会发现,当具有挂起写入的文件描述符被closures时,SIGPIPE就会发生。 尽pipe最终写入将返回-1,但信号的整个点是通知您asynchronous的写入不再可能。 这是UNIX中pipe道整体优雅协同结构的一部分。
现在,我可以在任何一本UNIX系统编程书籍中指出您的整个讨论,但是有一个更好的答案:您可以自己validation。 编写一个简单的B
程序[1] – 你已经有胆量了,你需要的只是一个main
,还有一些包括 – 为SIGPIPE
添加一个信号处理程序。 运行一个pipe道
cat | B | more
并在另一个terminal窗口中,将debugging器附加到B上,并在B信号处理程序中放置一个断点。
现在,杀了更多 ,B应该在你的信号处理程序中断。 检查堆栈。 你会发现读取仍在等待中。 让信号处理程序继续并返回,并查看写入返回的结果 – 然后将为-1。
[1]当然,你会用C写你的B程序。:-)
我认为这是要正确的error handling,而不需要在写入pipe道的所有内容中都有大量的代码。
有些程序忽略了write()
的返回值; 没有SIGPIPE
他们将无用地生成所有的输出。
检查write()
的返回值的程序如果失败,可能会打印一条错误消息; 这对于破损的pipe道是不合适的,因为它对整个pipe道来说并不是一个错误。
https://www.gnu.org/software/libc/manual/html_mono/libc.html
这个链接说:
pipe道或FIFO必须在两端同时打开。 如果您从没有任何进程写入的pipe道或FIFO文件(可能是因为它们全部closures了文件或退出)读取,则读取将返回文件结束。 写入没有读取过程的pipe道或FIFO被视为错误条件; 它会生成一个SIGPIPE信号,如果信号被处理或阻塞,将会以错误代码EPIPE失败。
– macros:INT SIGPIPE
pipe道破损。 如果您使用pipe道或FIFO,则必须devise应用程序,以便一个进程在另一个进程开始写入之前打开pipe道进行读取。 如果读取过程从不开始,或者意外终止,那么写入pipe道或FIFO会产生一个SIGPIPE信号。 如果SIGPIPE被阻止,处理或忽略,则违规呼叫将以EPIPE失败。
pipe道和FIFO特殊文件在Pipes和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++; } }
==================== //从stdin //readstdin.c只读取3个字符
# 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个字符后才会收到。
这是否certificateSIGPIPE
不是在读取过程结束后立即生成的,而是在尝试向闭合的pipe道写入更多的数据之后?