为什么存在SIGPIPE?

时间:2011-12-03 17:27:46

标签: posix

根据我的理解,SIGPIPE只能作为write()的结果出现,errno可以(并且确实)返回-1并将EPIPE设置为SIGPIPE。那么为什么我们有额外的信号开销呢?我每次使用烟斗时都会忽略{{1}}并且从未感到任何痛苦,我是否遗漏了什么?

5 个答案:

答案 0 :(得分:103)

我不买以前接受的答案。确切地生成SIGPIPEwriteEPIPE失败,而不是事先生成 - 事实上,一种安全的方法可以避免SIGPIPE而不改变全局信号处置,而是暂时用{pthread_sigmask屏蔽它{1}},执行write,然后执行sigtimedwait(零超时)以消除任何待处理的SIGPIPE信号(发送到调用线程,而不是进程),然后取消屏蔽再一次。

我相信SIGPIPE存在的原因要简单得多:为纯粹的“过滤器”程序建立理智的默认行为,这些程序不断读取输入,以某种方式转换输入,并写入输出。如果没有SIGPIPE,除非这些程序明确地处理写入错误并立即退出(这可能不是所有写入错误的所需行为,无论如何),它们将继续运行,直到它们输出耗尽,即使它们的输出管道已经关闭。当然,您可以通过显式检查SIGPIPE并退出来复制EPIPE的行为,但SIGPIPE的全部目的是在程序员懒惰时默认实现此行为。

答案 1 :(得分:25)

因为您的程序可能正在等待I / O或以其他方式暂停。 SIGPIPE异步中断您的程序,终止系统调用,因此可以立即处理。

<强>更新

考虑一个管道A | B | C

只是为了明确,我们假设B是规范的复制循环:

while((sz = read(STDIN,bufr,BUFSIZE))>=0)
    write(STDOUT,bufr,sz);
B在{em>读取(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信号处理程序中放置一个断点。

现在,杀死 more ,B应该在你的信号处理程序中断。检查堆栈。您会发现读取仍处于待处理状态。让信号处理程序继续并返回,并查看 write 返回的结果 - 然后为-1。

[1]当然,你将用C语言编写你的B程序: - )

答案 2 :(得分:7)

https://www.gnu.org/software/libc/manual/html_mono/libc.html

此链接说:

管道或FIFO必须同时在两端打开。如果您从没有任何进程写入的管道或FIFO文件中读取(可能是因为它们已关闭文件或退出),则读取将返回文件结尾。 写入没有读取过程的管道或FIFO被视为错误条件;如果处理或阻止信号,它会生成一个SIGPIPE信号并失败并显示错误代码EPIPE。

- Macro:int SIGPIPE

管子破了。如果使用管道或FIFO,则必须设计应用程序,以便一个进程在另一个进程开始写入之前打开管道进行读取。如果读取过程永远不会开始,或者意外终止,写入管道或FIFO会引发SIGPIPE信号。如果SIGPIPE被阻止,处理或忽略,则违规呼叫将失败,而不是EPIPE。

Pipes和FIFO特殊文件在Pipes和FIFO中有更详细的讨论。

答案 3 :(得分:5)

我认为这样做是为了使错误处理正确,而不需要在写入管道的所有内容中使用大量代码。

某些程序会忽略write()的返回值;没有SIGPIPE他们将无用地生成所有输出。

检查write()返回值的程序可能会在失败时打印错误消息;这对于管道损坏是不合适的,因为它对整个管道来说并不是一个错误。

答案 4 :(得分:2)

机器信息:

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