Implementación de un pipe entre dos procesos en C con fork, dup y redirecciones de E/S
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), dondefd[0]es el extremo de lectura yfd[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 conexecvp(). - 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 previamenteSTDOUT_FILENOoSTDIN_FILENO, se garantiza que dicho descriptor sea el estándar. - Es recomendable comprobar el valor de retorno de
dup()y utilizardup2()para mayor robustez. - Las expresiones
pid != waitpid(...)comparan en lugar de asignar; probablemente se pretendía escribirwaitpid(pid, &status, 0)sin comparación.