Recherche…


Introduction

Les tuyaux constituent un mécanisme de communication unidirectionnelle interprocessus ou interthread dans le cadre d'une seule machine. Logiquement, un tube se compose de deux extrémités connectées, une sur laquelle des données peuvent être écrites et une autre à partir de laquelle les données peuvent être lues ultérieurement, avec un tampon de données entre lesquelles des écritures et des lectures ne doivent pas nécessairement être synchrones. Les tuyaux doivent être distingués des conduites en coque, qui sont une application de tuyaux.

Création et utilisation de base

Les pipes anonymes, ou simplement pipes, sont des objets gérés par le noyau exposés à des processus sous la forme d'une paire de descripteurs de fichiers, un pour le terminal de lecture et un pour le terminus d'écriture. Ils sont créés via la fonction pipe (2):

int pipefds[2];
int result;

result = pipe(pipefds);

En cas de succès, pipe() enregistre le descripteur de l'extrémité de lecture du tube à l'index 0 du tableau fourni, et le descripteur de la fin de l'écriture à l'index 1; ces indices sont analogues aux numéros de descripteur de fichier classiques pour les flux standard.

Après avoir créé un tube, on utilise les fonctions d'E / S POSIX pour écrire à la fin de l'écriture ou à la lecture:

Processus 1:

ssize_t bytes_written = write(pipefds[1], "Hello, World!", 14);

Processus 2:

char buffer[256];
ssize_t bytes_read = read(pipefds[0], buffer, sizeof(buffer));

Alternativement, on peut utiliser fdopen() pour envelopper une des deux extrémités de tube dans une structure FILE pour les utiliser avec les fonctions de base:

FILE *write_end = fdopen(pipefds[1]);

if (write_end) {
    fputs("Hello, World!");
}

Les tuyaux ont des tampons d'E / S finis, et les écritures ordinaires sur les tuyaux avec des tampons complets seront bloquées. Les tuyaux ne sont donc pas un mécanisme sûr pour qu'un thread puisse communiquer avec lui-même, comme si le thread qui écrit dans un tube est également le seul à en lire, puis dès qu'un écriture bloque ce thread est bloqué.

Un canal persiste tant qu'un processus a une description de fichier ouverte pour l'une des extrémités du tuyau. La fonction close (2) peut être utilisée pour fermer une extrémité de tuyau représentée par un descripteur de fichier, et fclose (3) peut être utilisé pour fermer une extrémité de tuyau via un FILE entouré de celui-ci.

Établissement d'un canal vers un processus enfant

Les descripteurs de fichiers et les objets FILE sont des ressources par processus qui ne peuvent pas être échangées entre processus via des E / S ordinaires. Par conséquent, pour que deux processus distincts puissent communiquer via un canal anonyme, un ou les deux processus participants doivent hériter d'un tube ouvert du processus qui a créé le canal, qui doit donc être le processus parent ou un ancêtre plus distant. Le cas le plus simple est celui dans lequel un processus parent veut communiquer avec un processus enfant.

Étant donné que le processus enfant doit hériter de la description de fichier ouverte requise de son parent, le canal doit être créé en premier. Le parent fourche alors. Normalement, chaque processus sera strictement un lecteur ou strictement un écrivain; dans ce cas, chacun devrait fermer l'extrémité du tuyau qu'il n'a pas l'intention d'utiliser.

void demo() {
    int pipefds[2];
    pid_t pid;

    // Create the pipe
    if (pipe(pipefds)) {
        // error - abort ...
    }

    switch (pid = fork()) {
        case -1:
            // error - abort ...
            break;
        case 0:   /* child */
            close(pipefds[0]);
            write(pipefds[1], "Goodbye, and thanks for all the fish!", 37);
            exit(0);
        default:  /* parent */
            close(pipefds[1]);

            char buffer[256];
            ssize_t nread = read(pipefds[0], sizeof(buffer) - 1);

            if (nread >= 0) {
                buffer[nread] = '\0';
                printf("My child said '%s'\n", buffer);
            }

            // collect the child
            wait(NULL);

            break;
    }
}

Connexion de deux processus enfants via un tube

La connexion de deux processus enfants via un canal est effectuée en connectant chacun des deux enfants au parent via différentes extrémités du même tuyau. En règle générale, le parent ne participera pas à la conversation entre les enfants, il ferme donc ses copies des deux extrémités du tuyau.

int demo() {
    int pipefds[2];
    pid_t child1, child2;

    if (pipe(pipefds)) {
        // error - abort ...
    }

    switch (child1 = fork()) {
        case -1:
            // error - abort
            break;
        case 0:   /* child 1 */
            close(pipefds[0]);
            write(pipefds[1], "Hello, brother!", 15);
            exit(0);
        default:  /* parent */
            // nothing
    }

    switch (child1 = fork()) {
        case -1:
            // error - abort
            break;
        case 0:   /* child 2 */
            char buffer[256];
            ssize_t nread;

            close(pipefds[1]);
            nread = read(pipefds[0], buffer, sizeof(buffer) - 1);
            if (nread < 0) {
                // handle error
            } else {
                buffer[nread] = '\0';
                printf("My brother told me '%s'\n", buffer);
            }
            exit(0);
        default:  /* parent */
            // nothing
    }

    // Only the parent reaches this point
    close(pipefds[0]);
    close(pipefds[1]);
    if (child1 >= 0) {
        wait(NULL);
        if (child2 >= 0) {
            wait(NULL);
        }
    }
}

Créer un pipeline de style shell

Un pipeline de type shell se compose de deux processus ou plus, chacun avec sa sortie standard connectée à l'entrée standard de la suivante. Les connexions de sortie en entrée sont construites sur des tuyaux. Pour établir un ensemble de processus semblable à un pipeline, on crée des processus enfants connectés aux tuyaux, comme décrit dans un autre exemple, et utilise en outre la fonction dup2 (2) pour dupliquer chaque bout de tuyau sur le descripteur de fichier standard approprié de son processus. Il est généralement recommandé de fermer ensuite le descripteur de fichier d’origine du tube, en particulier si, comme c’est souvent le cas, l’enfant doit exécuter une commande différente.

// Most error handling omitted for brevity, including ensuring that pipe ends are
// always closed and child processes are always collected as needed; see other
// examples for more detail.
int demo() {
    int pipefds[2];
    pid_t child1 = -1, child2 = -1;

    pipe(pipefds);

    switch (child1 = fork()) {
        case -1:
            // handle error ...
            break;
        case 0:   /* child 1 */
            close(pipefds[0]);
            dup2(pipefds[1], STDOUT_FILENO);
            close(pipefds[1]);
            execl("/bin/cat", "cat", "/etc/motd", NULL);
            exit(1);  // execl() returns only on error
        default:  /* parent */
            // nothing
    }

    switch (child2 = fork()) {
        case -1:
            // handle error ...
            break;
        case 0:   /* child 2 */
            close(pipefds[1]);
            dup2(pipefds[0], STDIN_FILENO);
            close(pipefds[0]);
            execl("/bin/grep", "grep", "[Ww]ombat", NULL);
            exit(1);  // execl() returns only on error
        default:  /* parent */
            // nothing
    }

    // Only the parent reaches this point
    close(pipefds[0]);
    close(pipefds[1]);
    wait(NULL);
    wait(NULL);
}

L'une des erreurs les plus courantes dans la configuration d'un pipeline est d'oublier qu'un ou plusieurs des processus impliqués ferment les extrémités des tuyaux qu'il n'utilise pas. En règle générale, le résultat final devrait être que chaque extrémité de tuyau appartient à un seul processus et n'est ouverte que dans ce processus.

Dans un cas comme la fonction de démonstration ci-dessus, si le deuxième enfant ou le parent ne ferme pas pipefds[1] , le deuxième enfant sera suspendu, car grep continuera d'attendre la saisie jusqu'à ce qu'il détecte EOF, ce qui ne sera pas le cas. observé tant que la fin de l'écriture du canal reste ouverte dans n'importe quel processus, tel que le processus parent ou le deuxième enfant lui-même.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow