Buscar..


Introducción

Las tuberías son un mecanismo para la comunicación entre hilos o entre procesos unidireccional dentro del alcance de una sola máquina. Lógicamente, un conducto consta de dos extremos conectados, uno en el que se pueden escribir datos y otro desde el cual se pueden leer posteriormente esos datos, con un búfer de datos entre los que no se requiere que las escrituras y las lecturas sean síncronas. Las tuberías deben distinguirse de las tuberías de concha, que son una aplicación de tuberías.

Creación y uso básicos.

Las canalizaciones anónimas, o simplemente tuberías, son objetos gestionados por el kernel expuestos a procesos como un par de descriptores de archivos, uno para el término de lectura y otro para el término de escritura. Se crean a través de la función pipe (2):

int pipefds[2];
int result;

result = pipe(pipefds);

En caso de éxito, pipe() registra el descriptor para el final de lectura del conducto en el índice 0 de la matriz proporcionada, y el descriptor para el extremo de escritura en el índice 1; estos índices son análogos a los números de descriptores de archivos convencionales para las secuencias estándar.

Una vez creado un conducto, uno utiliza las funciones de E / S de POSIX para escribir en el extremo de escritura o leer desde el final de lectura:

Proceso 1:

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

Proceso 2:

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

Alternativamente, uno puede usar fdopen() para envolver uno de los dos extremos de la tubería en una estructura de FILE para usar con las funciones de C stdio:

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

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

Las tuberías tienen búferes de E / S finitos y se bloquearán las escrituras normales en tuberías con búferes completos. Por lo tanto, los conductos no son un mecanismo seguro para que un hilo se comunique consigo mismo, ya que si el hilo que se escribe en un tubo también es el único que lee, entonces, tan pronto como el bloque de escritura se bloquee, el hilo no se bloquea.

Una tubería persiste siempre que cualquier proceso tenga una descripción de archivo abierto para cualquiera de los extremos de la tubería. La función de close (2) se puede usar para cerrar un extremo de tubería representado por un descriptor de archivo, y fclose (3) se puede usar para cerrar un extremo de tubería a través de un FILE envuelto alrededor de él.

Estableciendo una tubería para un proceso hijo.

Los descriptores de archivo y los objetos de FILE son recursos por proceso que no pueden intercambiarse entre procesos a través de la E / S ordinaria. Por lo tanto, para que dos procesos distintos se comuniquen a través de un conducto anónimo, uno o ambos procesos participantes deben heredar un extremo de conducto abierto del proceso que creó el conducto, que debe ser el proceso principal o un antepasado más lejano. El caso más simple es aquel en el que un proceso padre quiere comunicarse con un proceso hijo.

Debido a que el proceso hijo debe heredar la descripción de archivo abierto necesaria de su padre, primero se debe crear el conducto. El padre entonces se bifurca. Normalmente, cada proceso será estrictamente un lector o estrictamente un escritor; en ese caso, cada uno debe cerrar el extremo del tubo que no pretende usar.

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;
    }
}

Conectando dos procesos hijos a través de una tubería.

La conexión de dos procesos secundarios a través de una tubería se realiza conectando cada uno de los dos hijos al padre a través de diferentes extremos de la misma tubería. Por lo general, el padre no será parte de la conversación entre los niños, por lo que cierra sus copias de los dos extremos de la tubería.

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

Creando una tubería de estilo shell

Una tubería de estilo shell consta de dos o más procesos, cada uno con su salida estándar conectada a la entrada estándar del siguiente. Las conexiones de salida a entrada están construidas en tuberías. Para establecer un conjunto de procesos similar a una tubería, uno crea procesos secundarios conectados a la tubería como se describe en otro ejemplo, y además utiliza la función dup2 (2) para duplicar cada extremo de la tubería en el descriptor de archivo estándar apropiado de su proceso. En general, es una buena idea cerrar el descriptor de archivo final de la tubería original, especialmente si, como suele ser el caso, uno tiene la intención de que el hijo ejecute un comando diferente.

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

Uno de los errores más comunes en la configuración de una tubería es olvidar que uno o más de los procesos involucrados cierran los extremos de las tuberías que no está utilizando. Como regla general, el resultado final debe ser que cada extremo de tubería sea propiedad de exactamente un proceso, y que solo esté abierto en ese proceso.

En un caso como la función de demostración anterior, si el segundo hijo o el padre no cierran las pipefds[1] , el segundo hijo quedará colgado, ya que grep continuará esperando la entrada hasta que vea EOF, y eso no será observado siempre que el extremo de escritura de la tubería permanezca abierto en cualquier proceso, como el proceso padre o el segundo hijo.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow