ti-enxame.com

Por que o SIGPIPE existe?

Pelo meu entendimento, SIGPIPE só pode ocorrer como resultado de uma write(), que pode (e) retorna -1 e define errno como EPIPE... Então, por que temos a sobrecarga extra de um sinal? Toda vez que trabalho com tubos, ignoro SIGPIPE e nunca senti dor como resultado, estou perdendo alguma coisa?

88
Shea Levy

Não compro a resposta previamente aceita. SIGPIPE é gerado exatamente quando o write falha com EPIPE, não de antemão - na verdade, uma maneira segura de evitar SIGPIPE sem alterar as disposições globais do sinal é mascará-lo temporariamente com pthread_sigmask, execute write, depois execute sigtimedwait (com tempo limite zero) para consumir qualquer sinal SIGPIPE pendente (enviado ao encadeamento de chamada, não ao processo) antes de desmascarar isso de novo.

Eu acredito que a razão SIGPIPE existe é muito mais simples: estabelecer um comportamento padrão sensato para programas puros de "filtro" que leem continuamente a entrada, a transformam de alguma forma e escrevem a saída. Sem SIGPIPE, a menos que esses programas lidem explicitamente com erros de gravação e imediatamente saiam (que pode não ser o comportamento desejado para todos os erros de gravação), eles continuarão em execução até ficar sem entrada, mesmo que seu canal de saída tenha sido fechadas. Claro que você pode duplicar o comportamento de SIGPIPE verificando explicitamente EPIPE e saindo, mas o objetivo de SIGPIPE era atingir esse comportamento por padrão quando o programador é preguiçoso.

106
R..

Porque seu programa pode estar esperando por E/S ou de outra forma suspenso. Um SIGPIPE interrompe seu programa de forma assíncrona, encerrando a chamada do sistema e, portanto, pode ser tratado imediatamente.

Atualização

Considere um pipeline A | B | C.

Apenas por definição, vamos assumir que B é o loop de cópia canônico:

while((sz = read(STDIN,bufr,BUFSIZE))>=0)
    write(STDOUT,bufr,sz);

B é bloqueado na chamada de leitura (2) que aguarda os dados de A quando C termina. Se você esperar o código de retorno de write (2) , quando B o verá? A resposta, é claro, não é até A escrever mais dados (o que poderia ser uma longa espera - e se A for bloqueado por outra coisa?). Observe, a propósito, que isso também nos permite um programa mais simples e limpo. Se você dependesse do código de erro da gravação, seria necessário algo como:

while((sz = read(STDIN,bufr,BUFSIZE))>=0)
    if(write(STDOUT,bufr,sz)<0)
        break;

Outra atualização

Aha, você está confuso sobre o comportamento da gravação. Você vê, quando o descritor de arquivo com a gravação pendente é fechado, o SIGPIPE acontece imediatamente. Enquanto a gravação retornará -1 eventualmente , o objetivo do sinal é notificá-lo de forma assíncrona que a gravação não é mais possível. Isso faz parte do que faz toda a elegante estrutura co-rotineira de tubos funcionar no UNIX.

Agora, eu poderia apontar uma discussão completa em qualquer um dos vários livros de programação de sistemas UNIX, mas há uma resposta melhor: você mesmo pode verificar isso. Escreva um programa simples B [1] - você já tem coragem, tudo que você precisa é um main e alguns incluem - e adicione um manipulador de sinal para SIGPIPE. Execute um pipeline como

cat | B | more

e em outra janela do terminal, conecte um depurador ao B e coloque um ponto de interrupção dentro do manipulador de sinal B.

Agora, mate o more e B deve quebrar no seu manipulador de sinal. examine a pilha. Você verá que a leitura ainda está pendente. deixe o manipulador de sinal prosseguir e retornar e observe o resultado retornado por write - que irá e depois seja -1.

[1] Naturalmente, você escreverá seu programa B em C. :-)

24
Charlie Martin

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

Este link diz:

Um tubo ou FIFO deve ser aberto nas duas extremidades simultaneamente. Se você ler de um canal ou arquivo FIFO que não possui nenhum processo gravado nele (talvez porque todos tenham fechado o arquivo ou tenham saído), a leitura retornará o final do arquivo. Gravar em um pipe ou FIFO que não possui um processo de leitura é tratado como uma condição de erro; gera um sinal SIGPIPE e falha com o código de erro EPIPE se o sinal for tratado ou bloqueado.

- Macro: int SIGPIPE

Cano quebrado. Se você usar pipes ou FIFOs, precisará projetar seu aplicativo para que um processo abra o pipe para leitura antes que outro comece a escrever. Se o processo de leitura nunca iniciar ou terminar inesperadamente, gravando no canal ou FIFO gera um sinal SIGPIPE. Se o SIGPIPE estiver bloqueado , manipulada ou ignorada, a chamada incorreta falha com o EPIPE.

Pipes e arquivos especiais FIFO são discutidos em mais detalhes em Pipes e FIFOs.

7
abc

Eu acho que é para corrigir o tratamento de erros sem exigir muito código em tudo o que é escrito em um pipe.

Alguns programas ignoram o valor de retorno de write(); sem SIGPIPE, eles gerariam inutilmente toda a saída.

Os programas que verificam o valor de retorno de write() provavelmente imprimem uma mensagem de erro se falhar; isso é inadequado para um tubo quebrado, pois não é realmente um erro para todo o pipeline.

5
jilles

Informações da máquina:

Linux 3.2.0-53-generic # 81-Ubuntu SMP Qui 22 de agosto 21:01:03 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

gcc versão 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)

Eu escrevi este código abaixo:

// 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);

}

Resultado:

$ ./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

Você pode ver que em todas as instâncias SIGPIPE só é recebida depois que mais de 3 caracteres são (tentou ser) gravados pelo processo de gravação.

Isso não prova que SIGPIPE não é gerado imediatamente após o término do processo de leitura, mas após uma tentativa de gravar mais alguns dados em um canal fechado?

2
abc