web-dev-qa-db-fra.com

Pourquoi SIGPIPE existe-t-il?

A ma connaissance, SIGPIPE ne peut se produire qu'à la suite d'une write(), qui peut (et doit) renvoyer -1 et mettre errno à EPIPE ... Alors pourquoi avons-nous la surcharge d'un signal? Chaque fois que je travaille avec des pipes, j'ignore SIGPIPE et je n'ai jamais ressenti de douleur. Quelque chose me manque-t-il?

88
Shea Levy

Je n'achète pas la réponse précédemment acceptée. SIGPIPE est généré exactement lorsque write échoue avec EPIPE, pas auparavant - en fait, un moyen sûr d'éviter SIGPIPE sans modifier les dispositions des signaux globaux est de temporairement masquez-le avec pthread_sigmask, exécutez write, puis sigtimedwait (avec zéro délai) pour consommer tout signal en attente SIGPIPE (qui est envoyé au thread appelant, pas au processus) avant le démasquant à nouveau.

Je pense que la raison pour laquelle SIGPIPE existe est beaucoup plus simple: établir un comportement sain par défaut pour les programmes "filtres" purs qui lisent continuellement les entrées, les transforment d'une manière ou d'une autre et écrivent les sorties. Sans SIGPIPE, à moins que ces programmes gèrent explicitement les erreurs d’écriture et s’arrêtent immédiatement (ce qui peut ne pas être le comportement souhaité pour toutes les erreurs d’écriture, de toute façon), ils continueront de fonctionner jusqu’à épuisement des entrées même si leur canal de sortie est épuisé. été fermé. Bien sûr, vous pouvez dupliquer le comportement de SIGPIPE en vérifiant explicitement EPIPE et en le quittant, mais l'objectif principal de SIGPIPE était d'obtenir ce comportement par défaut lorsque le programmeur est paresseux.

106
R..

Parce que votre programme est peut-être en attente d'E/S ou suspendu d'une autre manière. Un SIGPIPE interrompt votre programme de manière asynchrone, mettant fin à l'appel système, et peut donc être traité immédiatement.

Mise à jour

Considérons un pipeline A | B | C.

Juste pour être précis, supposons que B est la boucle de copie canonique:

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

B est bloqué sur l'appel read (2) en attente de données provenant de A quand C se termine. Si vous attendez le code retour de write (2) , quand B le verra-t-il? La réponse, bien sûr, n’est pas tant que A n’écrit plus de données (ce qui pourrait être une longue attente - et si A est bloqué par autre chose?). Notez, en passant, que cela nous permet également de créer un programme plus simple et plus propre. Si vous dépendez du code d'erreur en écriture, vous aurez besoin de quelque chose comme:

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

ne autre mise à jour

Aha, vous êtes confus sur le comportement de l'écriture. Vous voyez, lorsque le descripteur de fichier avec l'écriture en attente est fermé, le SIGPIPE se passe juste à ce moment-là. Bien que l'écriture retourne -1 en fin de compte , le but du signal est de vous informer de manière asynchrone que l'écriture n'est plus possible. Cela fait partie de ce qui fait que toute la co-routine élégante des tuyaux fonctionne sous UNIX.

Maintenant, je pourrais vous diriger vers une discussion entière dans n’importe quel livre de programmation système UNIX, mais la réponse est meilleure: vous pouvez le vérifier vous-même. Ecrivez un simple programme B [1] - vous avez déjà le courage, tout ce dont vous avez besoin est un main et quelques inclus - et ajoutez un gestionnaire de signal pour SIGPIPE. Exécuter un pipeline comme

cat | B | more

et dans une autre fenêtre de terminal, attachez un débogueur à B et placez un point d'arrêt à l'intérieur du gestionnaire de signaux B.

Maintenant, tuez le plus et B devrait casser dans votre gestionnaire de signal. examinez la pile. Vous constaterez que la lecture est toujours en attente. laissez le gestionnaire de signaux procéder et revenir, et regardez le résultat renvoyé par write - qui alors soit -1.

[1] Naturellement, vous écrirez votre programme B en C. :-)

24
Charlie Martin

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

Ce lien dit:

Un tube ou FIFO doit être ouvert aux deux extrémités simultanément. Si vous lisez à partir d'un canal ou FIFO, aucun processus ne l'écrit) (peut-être parce qu'ils ont tous fermé le fichier ou quitté), la lecture retourne la fin du fichier. L'écriture dans un tube ou FIFO qui ne le fait pas ") t avoir un processus de lecture est traité comme une condition d’erreur, il génère un signal SIGPIPE, et échoue avec le code d’erreur EPIPE si le signal est traité ou bloqué.

- Macro: int SIGPIPE

Tuyau cassé. Si vous utilisez des tubes ou des FIFO, vous devez concevoir votre application de sorte qu'un processus ouvre le canal en lecture avant qu'un autre ne commence à écrire. Si le processus de lecture ne commence jamais ou ne se termine pas de manière inattendue, écrit dans le tuyau ou FIFO génère un signal SIGPIPE. If SIGPIPE est bloqué, géré ou ignoré, l’appel en cause échoue avec EPIPE.

Les pipes et les fichiers spéciaux FIFO sont discutés plus en détail dans les pipes et les FIFOs).

7
abc

Je pense que cela consiste à corriger le traitement des erreurs sans requérir beaucoup de code dans tout ce qui est écrit dans un tube.

Certains programmes ignorent la valeur de retour de write(); sans SIGPIPE, ils généreraient inutilement toutes les sorties.

Les programmes qui vérifient la valeur de retour de write() impriment probablement un message d'erreur en cas d'échec; ceci est inapproprié pour un tuyau cassé car ce n'est pas vraiment une erreur pour tout le pipeline.

5
jilles

Info machine:

Linux 3.2.0-53-generic # 81-Ubuntu SMP jeu. 22 août 21:01:03 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)

J'ai écrit ce code ci-dessous:

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

}

Sortie:

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

Vous pouvez voir que, dans chaque cas, SIGPIPE n'est reçu qu'après que plus de 3 caractères ont été (essayés d'être) écrits par le processus d'écriture.

Cela ne prouve-t-il pas que SIGPIPE n'est pas généré immédiatement après la fin du processus de lecture, mais après une tentative d'écriture de données supplémentaires dans un canal fermé?

2
abc