97 votes

Pourquoi ne SIGPIPE existent?

De ma compréhension, SIGPIPE peut se produire uniquement comme le résultat d'un write(), ce qui peut (et ne doit) renvoie -1 et définissez errno de 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 aucune douleur à la suite, ai-je raté quelque chose?

122voto

R.. Points 93718

Je n'ai pas acheter la précédemment accepté de répondre. SIGPIPE est généré exactement quand l' write d'échec avec EPIPE, pas à l'avance - en fait, un moyen sûr pour éviter SIGPIPE sans modification du signal global dispositions est temporairement masque il avec pthread_sigmask, effectuez l' write, puis effectuez sigtimedwait (avec zéro délai d'attente) à consommer tout en attendant SIGPIPE (signal qui est envoyé au thread appelant, et non pas le processus) avant de les démasquer à nouveau.

Je crois que la raison SIGPIPE existe, c'est beaucoup plus simple: l'établissement de sane comportement par défaut pour un pur "filtre" des programmes qui ne cesse de lire l'entrée, de le transformer en quelque sorte, et écrire la sortie. Sans SIGPIPE, à moins que ces programmes de traiter explicitement les erreurs d'écriture et de quitter immédiatement (ce qui pourrait ne pas être le comportement souhaité pour toutes les erreurs d'écriture, de toute façon), ils vont continuer à fonctionner jusqu'à épuisement de l'entrée, même si leur sortie de la pipe a été fermé. Bien sûr, vous pouvez duplicte le comportement de l' SIGPIPE explicitement à la vérification de EPIPE et à la sortie, mais le but entier de SIGPIPE a été pour obtenir ce comportement par défaut lorsque le programmeur est paresseux.

24voto

Charlie Martin Points 62306

Parce que votre programme peut-être en attente d'e/S ou sinon suspendu. Un SIGNAL interrompt votre programme de manière asynchrone, la terminaison de l'appel système, et qui peuvent donc être traitées immédiatement.

Mise à jour

Considérons un pipeline A | B | C.

Juste pour la précision, nous allons supposer que B est la copie canonique de la boucle:

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

B , est bloqué sur la lecture(2) appel en attente pour les données d' A lorsque C se termine. Si vous attendez que le code de retour de write(2), quand B voir? La réponse, bien sûr, n'est pas jusqu'à ce qu'Un écrit plus de données (qui peut être une longue attente-si A est bloqué par quelque chose d'autre?). Remarquez, en passant, que cela nous permet aussi une plus simple, plus propre programme. Si vous dépendait le code d'erreur d'écriture, vous auriez 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 sur le comportement de l'écriture. Vous voyez, lorsque le descripteur de fichier avec l'attente de l'écriture est fermé, le SIGNAL qui arrive à droite, puis. Alors que l'écriture sera de retour -1 finalement, le point de l'ensemble du signal est de vous informer de manière asynchrone que l'écriture n'est plus possible. C'est une partie de ce qui rend l'ensemble élégant de la co-structure de routine de tuyaux de travail sous UNIX.

Maintenant, je pourrais vous pour une discussion de plusieurs de programmation système sous UNIX livres, mais il y a une meilleure solution: vous pouvez vérifier vous-même. Écrire un simple B program[1], vous avez le courage, déjà, tous vous avez besoin est un main et certains inclut -- et ajouter un gestionnaire de signal pour SIGPIPE. Exécuter un pipeline comme

cat | B | more

et un autre dans une fenêtre de terminal, attacher un débogueur de B et de mettre un point d'arrêt à l'intérieur de la B gestionnaire de signal.

Maintenant, tuer le plus et le B doit briser dans votre gestionnaire de signal. examiner la pile. Vous constaterez que la lecture est toujours en attente. laissez le gestionnaire de signal de procéder et de retour, et regardez le résultat retourné par écrire -- qui va ensuite être -1.

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

7voto

abc Points 2888

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

Ce lien dit:

Un tuyau ou une FIFO doit être ouvert à ses deux extrémités simultanément. Si vous lisez à partir d'un tuyau ou un fichier FIFO qui n'ont pas de processus d'écriture (peut-être parce qu'ils ont tous fermé le fichier, ou de sortie), la lecture des retours de fin de fichier. L'écriture d'un tube ou d'une FIFO qui ne dispose pas d'un processus de lecture est considérée comme une condition d'erreur; il génère un signal SIGPIPE, et échoue avec le code d'erreur EPIPE si le signal est manipulé ou bloqué.

- Macro: int signal SIGPIPE

Broken pipe. Si vous utilisez des tuyaux ou Fifo, vous devez concevoir votre application pour qu'un processus ouvre le tuyau pour la lecture avant qu'un autre commence à écrire. Si la lecture ne démarre pas ou s'arrête de façon inattendue, de l'écriture à la pipe ou FIFO soulève un signal SIGPIPE. Si le SIGNAL est bloqué, traitée ou négligée, la délinquance appel échoue avec EPIPE à la place.

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

5voto

jilles Points 4241

Je pense que c'est pour obtenir la gestion d'erreur correct sans nécessiter beaucoup de code dans tout écrit à un tuyau.

Certains programmes d'ignorer la valeur de retour de l' write(); sans SIGPIPE ils seraient inutilement générer toutes les sorties.

Programmes de vérifier la valeur de retour de l' write() de chances d'imprimer un message d'erreur s'il échoue, c'est inapproprié pour une fracture de la pipe comme elle n'est pas vraiment une erreur pour l'ensemble de la canalisation.

2voto

abc Points 2888

Machine d'info:

Linux 3.2.0-53-generic #81-Ubuntu SMP Thu Aug 21 22:01:03 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

la version de 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++;

    }

}

==================== //Lit seulement 3 caractères à partir de stdin. //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 instance de signal n'est reçu après plus de 3 caractères sont (essayé de ) écrit par le processus d'écriture.

N'est-ce pas prouver qu' SIGPIPE n'est pas généré immédiatement après la lecture de processus se termine, mais après une tentative d'écrire un peu plus de données à un tuyau?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X