Pourquoi SIGPIPE existe-t-il?

D'après ma compréhension, SIGPIPE ne peut se produire que comme le résultat d'un write(), qui peut (et fait) retourner -1 et définir errno à EPIPE... Alors, pourquoi avons-nous la surcharge d'un signal? Chaque fois que je travaille avec des tuyaux, j'ignore SIGPIPE et n'ai jamais ressenti de douleur en conséquence, est-ce que je manque quelque chose?

83
demandé sur user 2011-12-03 21:27:46

5 réponses

Je n'achète pas la réponse précédemment acceptée. {[0] } est généré exactement lorsque le write échoue avec EPIPE, pas au préalable - en fait, un moyen sûr d'éviter SIGPIPE sans modifier les dispositions globales du signal est de le masquer temporairement avec pthread_sigmask, effectuer le write, puis effectuer sigtimedwait (avec un délai d'attente nul) pour consommer tout signal SIGPIPE en attente (qui est envoyé au thread appelant, pas

Je crois que la raison pour laquelle SIGPIPE existe est beaucoup plus simple: établir un comportement par défaut sain pour les programmes "filtre" purs qui lisent en continu l'entrée, la transforment en quelque sorte et écrivent la sortie. Sans SIGPIPE, à moins que ces programmes ne gèrent explicitement les erreurs d'écriture et ne quittent immédiatement (ce qui peut ne pas être le comportement souhaité pour toutes les erreurs d'écriture, de toute façon), ils continueront à fonctionner jusqu'à ce qu'ils soient à court d'entrée même si leur tuyau de sortie a été fermé. Bien sûr, vous pouvez dupliquer le comportement de SIGPIPE en vérifiant explicitement EPIPE et en sortant, mais l'ensemble le but de SIGPIPE était d'atteindre ce comportement par défaut lorsque le programmeur est paresseux.

103
répondu R.. 2016-10-26 09:13:47

Parce que votre programme peut être en attente d'E / S ou autrement suspendu. 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 la définition, nous supposerons que B est la boucle de copie canonique:

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

B est bloqué sur l'appelread(2) en attente des données de A Lorsque C se termine. Si vous attendez le retour code de write (2) , Quand B le verra-t-il? La réponse, bien sûr, n'est pas jusqu'à ce que A é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 un programme plus simple et plus propre. Si vous dépendez du code d'erreur de write, vous aurez besoin de quelque chose comme:

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

Une Autre mise à jour

Aha, vous êtes confus au sujet du comportement de l'écriture. Vous voyez, lorsque le descripteur de fichier avec l'attente write est fermé, le SIGPIPE arrive à ce moment-là. Alors que l'écriture retournera -1 éventuellement , 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 structure co-routine élégante des pipes fonctionne sous UNIX.

Maintenant, je pourrais vous indiquer toute une discussion dans l'un des nombreux livres de programmation du système UNIX, mais il y a une meilleure réponse: vous pouvez le vérifier vous-même. Ecrire un programme simple B [1] -- vous avez déjà le courage, tout ce dont vous avez besoin est un main et certains incluent-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 dans le gestionnaire de signal B.

Maintenant, tuez le Plus et B devrait casser dans votre gestionnaire de signal. examinez la pile. Vous constaterez que la lecture {[22] } est toujours en attente. laissez le gestionnaire de signaux continuer et revenir, et regardez le résultat retourné par écrire, -- qui sera , puis -1 être.

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

25
répondu Charlie Martin 2011-12-03 22:08:57

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

Ce lien indique:

Un tuyau ou FIFO doit être ouvert aux deux extrémités simultanément. Si vous lisez à partir d'un fichier pipe ou FIFO qui n'a aucun processus qui l'écrit (peut-être parce qu'ils ont tous fermé le fichier ou quitté), la lecture renvoie la fin du fichier. L'écriture dans un tuyau ou FIFO qui n'a pas de processus de lecture est traitée comme une condition d'erreur; elle génère un signal SIGPIPE, et échoue avec le code D'erreur EPIPE si le signal est manipulé ou bloqué.

- Macro: INT SIGPIPE

Tuyau cassé. Si vous utilisez des tuyaux ou des FIFO, vous devez concevoir votre application de sorte qu'un processus ouvre le tuyau pour la lecture avant qu'un autre commence à écrire. Si le processus de lecture ne démarre jamais, ou se termine de façon inattendue, l'écriture dans le tuyau ou FIFO déclenche un signal SIGPIPE.{[8] } si SIGPIPE est bloqué, géré ou ignoré, L'appel incriminé échoue avec EPIPE à la place.

Pipes et FIFO les fichiers spéciaux sont discutés plus en détail dans les tuyaux et les FIFO.

6
répondu abc 2013-09-24 02:12:27

Je pense que c'est pour que la gestion des erreurs soit correcte sans nécessiter beaucoup de code dans tout ce qui est écrit dans un tuyau.

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 s'il échoue; ceci est inapproprié pour un tuyau cassé car ce n'est pas vraiment une erreur pour l'ensemble du pipeline.

5
répondu jilles 2011-12-03 20:44:58

Informations sur la Machine:

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

Version Gcc 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 exemple SIGPIPE n'est reçu après plus de 3 caractères sont (essayé d'être) écrit 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'écrire plus de données dans un tuyau fermé?

1
répondu abc 2018-07-18 07:35:35