Implementación de un pipe entre dos procesos en C con fork, dup y redirecciones de E/S

Informática y Telecomunicaciones5,73 KB

Implementación de un pipe entre dos procesos en C

A continuación se muestra el fragmento de código correspondiente al caso en que el intérprete de comandos recibe dos mandatos conectados mediante una tubería (pipe). Cada mandato se ejecuta en un proceso hijo distinto, y la salida estándar del primero se redirige a la entrada estándar del segundo.

Código fuente

else if(num_commands==2){

    /* Declaro las variables */
    int fd[2]; /* Descriptores para el pipe */
    pipe(fd);
    pid_t pid, pid2; /* Declaro 2 hijos */
    int status, fich, errorRedir=0;

    if (pipe(fd) < 0) {
        perror("pipe");
        exit(-1);
    }

    /* HIJO 1 */
    pid = fork();
    switch(pid) {
        case -1: /* error */
            perror("fork");
            exit(-1);
        case 0: /* proceso hijo ejecuta */
            /* printf ("Child: %i\n ", getpid()); */
            close(fd[0]); /* cierra el extremo de lectura del pipe */
            close(STDOUT_FILENO); /* cierra la salida estándar */
            dup(fd[1]); /* duplica el extremo de escritura del pipe sobre STDOUT */
            close(fd[1]); /* cierra el extremo de escritura original */
            execvp(argvv[0][0], argvv[0]);
            perror("Error");
            break;

        default:
            /* HIJO 2 */
            pid2 = fork();
            switch(pid2) {
                case -1: /* error */
                    perror("fork");
                    exit(-1);
                case 0: /* proceso hijo ejecuta */
                    printf ("Child2: %i\n ", getpid());
                    close(fd[1]); /* cierra el extremo de escritura del pipe */
                    close(STDIN_FILENO); /* cierra la entrada estándar */
                    dup(fd[0]); /* duplica el extremo de lectura del pipe sobre STDIN */
                    close(fd[0]); /* cierra el extremo de lectura original */

                    if(filev[0] != NULL) {
                        fich = open(filev[0], O_RDONLY);

                        if (fich == -1) {
                            perror("Error");
                            errorRedir = 1; /* marca error de redirección */
                        }
                        close(STDIN_FILENO); /* se cierra la entrada estándar */
                        dup(fich); /* redireccionamos la entrada al fichero */
                        close(fich); /* cerramos el descriptor de fichero */
                    }

                    if(filev[1] != NULL) {
                        fich = open(filev[1], O_CREAT|O_TRUNC|O_WRONLY, 0777);

                        if (fich == -1) {
                            perror("Error");
                            errorRedir = 1;
                        }
                        close(STDOUT_FILENO); /* se cierra la salida estándar */
                        dup(fich); /* redireccionamos la salida al fichero */
                        close(fich); /* cerramos el descriptor de fichero */
                    }

                    if(filev[2] != NULL) {
                        fich = open(filev[2], O_CREAT|O_TRUNC|O_WRONLY, 0777);

                        if (fich == -1) {
                            perror("Error");
                            errorRedir = 1;
                        }
                        close(STDERR_FILENO); /* se cierra la salida de error estándar */
                        dup(fich); /* redireccionamos la salida de error al fichero */
                        close(fich); /* cerramos el descriptor de fichero */
                    }

                    if(errorRedir == 0) {
                        execvp(argvv[1][0], argvv[1]);
                        perror("Error");
                    }

                    break;

                default:
                    close(fd[0]);
                    close(fd[1]);

                    pid != waitpid(pid, &status, 0); /* espero al hijo 1 */
                    /* printf ("Wait child: %i\n ", pid); */
                    pid2 != waitpid(pid2, &status, 0); /* espero al hijo 2 */
                    printf ("Wait child2: %i\n ", pid2);
            }
    }

} /* fin num_commands==2 */

Descripción del funcionamiento

  • Creación del pipe: se crea una tubería con pipe(fd), donde fd[0] es el extremo de lectura y fd[1] el de escritura.
  • Primer hijo: cierra el extremo de lectura, redirige su salida estándar al extremo de escritura del pipe mediante dup() y ejecuta el primer mandato con execvp().
  • Segundo hijo: cierra el extremo de escritura, redirige su entrada estándar al extremo de lectura del pipe y, si existen, aplica las redirecciones de fichero indicadas en filev (entrada, salida y salida de error).
  • Proceso padre: cierra ambos extremos del pipe para que el lector reciba el fin de fichero (EOF) cuando el escritor termine, y espera a la finalización de ambos hijos con waitpid().

Aspectos importantes

  • El padre debe cerrar ambos extremos del pipe; de lo contrario, el segundo proceso no recibiría EOF y quedaría bloqueado indefinidamente.
  • La función dup() devuelve el nuevo descriptor más bajo disponible; al cerrar previamente STDOUT_FILENO o STDIN_FILENO, se garantiza que dicho descriptor sea el estándar.
  • Es recomendable comprobar el valor de retorno de dup() y utilizar dup2() para mayor robustez.
  • Las expresiones pid != waitpid(...) comparan en lugar de asignar; probablemente se pretendía escribir waitpid(pid, &status, 0) sin comparación.

Descargar

PDF Word HTML