
Aris Kantas
Tengo que escribir un shell que pueda ejecutar tuberías. Por ejemplo, comandos como ls -l | wc -l
“. He analizado con éxito el comando dado por el usuario de la siguiente manera:
“ls” = primercmd
“-l” = frsarg
“wc” = scmd
“-l” = cargar
Ahora tengo que usar dos tenedores ya que los comandos son dos y una pipa. El bloque de código que escribí para ejecutar el comando es el siguiente:
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if(pid==0)
{
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[READ_END]);
execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
}
else
{
pid=fork();
if(pid==0)
{
dup2(fd[READ_END], STDIN_FILENO);
close(fd[WRITE_END]);
execlp(scmd, scmd, secarg, (char*) NULL);
}
}
Entonces, cuando ejecuto mi shell e ingreso el comando ls -l | wc -l
(por ejemplo) el resultado de los execs no aparece pero el shell sigue funcionando normalmente.
Lo extraño es que el resultado del comando se muestra solo cuando termino mi shell con “exit” o “^C”.
¿Qué tiene de malo esa salida? ¿Por qué no aparece justo después de ingresar el comando?

usuario1969104
Debe cerrar todos los descriptores de tubería tanto en el proceso principal como en el proceso secundario (después de la duplicación en el proceso secundario). En su código, el problema principal es que, el wc
El proceso no finaliza porque todavía hay escritores presentes (ya que el proceso principal no ha cerrado el final de escritura). Los cambios se muestran a continuación. También he añadido el waitpid
en el proceso padre para esperar el wc
proceso.
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if(pid==0)
{
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[READ_END]);
close(fd[WRITE_END]);
execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
fprintf(stderr, "Failed to execute '%s'\n", firstcmd);
exit(1);
}
else
{
pid=fork();
if(pid==0)
{
dup2(fd[READ_END], STDIN_FILENO);
close(fd[WRITE_END]);
close(fd[READ_END]);
execlp(scmd, scmd, secarg,(char*) NULL);
fprintf(stderr, "Failed to execute '%s'\n", scmd);
exit(1);
}
else
{
int status;
close(fd[READ_END]);
close(fd[WRITE_END]);
waitpid(pid, &status, 0);
}
}

islam
Mmmm, lo suficientemente cerca. Echas de menos manejar el cierre de algún descriptor de archivo después de la bifurcación.
Aquí hay alguna referencia:
- sobre pipa, http://unixwiz.net/techtips/remap-pipe-fds.html
- Acerca del descriptor de archivo,
http://www.usna.edu/Users/cs/aviv/classes/ic221/s14/lec/09/lec.html
Aquí está mi código:
#include <fcntl.h> //
#include <stdio.h> //
#include <stdlib.h> //
#include <string.h> //
#include <sys/types.h> //
#include <sys/wait.h> //
#include <sys/stat.h> //
#include <termios.h> //
#include <unistd.h> //
//
#define INPUT_END 1 // INPUT_END means where the pipe takes input
#define OUTPUT_END 0 // OUTPUT_END means where the pipe produces output
//
int main(int argc, char* argv[]) //
{ //
pid_t pid1; // [STDIN -> terminal_input, STDOUT -> terminal_output] (of the parent process)
pid_t pid2; //
int fd[2]; //
//
pipe(fd); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input, fd[1] -> pipe_output]
pid1 = fork(); //
//
if(pid1==0) //
{ // I am going to be the wc process (i.e. taking input from the pipe)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[1] -> pipe_output] (of the WC process)
dup2(fd[OUTPUT_END], STDIN_FILENO); // [STDIN -> pipe_output, STDOUT -> terminal_output, fd[1] -> pipe_output] (of the WC process)
close(fd[OUTPUT_END]); // [STDIN -> pipe_output, STDOUT -> terminal_output] (of the WC process)
execlp("wc", "wc", "-l",(char*) NULL); //
} //
else //
{ //
pid2=fork(); //
//
if(pid2==0) //
{ // I am going to be the ls process (i.e. producing output to the pipe)
close(fd[OUTPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input] (of the ls process)
dup2(fd[INPUT_END], STDOUT_FILENO); // [STDIN -> terminal_input, STDOUT -> pipe_input, fd[0] -> pipe_input] (of the ls process)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> pipe_input] (of the ls process)
execlp("ls","ls","-l",(char*) NULL); //
} //
//
close(fd[OUTPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input] (of the parent process)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output] (of the parent process)
waitpid(-1, NULL, 0); // As the parent process - we wait for a process to die (-1) means I don't care which one - it could be either ls or wc
waitpid(-1, NULL, 0); // As the parent process - we wait for the another process to die.
// At this point we can safely assume both process are completed
} //
} //

Delphoenyx
Bueno, un trabajo simple y eficiente para algo como esto es hacer un script con las cosas de la tubería y luego llamar al script con algún comando exec desde su código C.
script.sh
#!/bin/sh
ls -l | wc -l
Y lo que acaba de hacer en su programa C algo como esto
char *argv[] = {"script.sh", NULL};
execv(argv[0], argv);
Tenga en cuenta que deberá tener script.sh en el mismo directorio de su programa C.
Debe cerrar los FD de tubería en los procesos principales.
– Barmar
24 de noviembre de 2015 a las 2:19
¿Puedes editar mi código para ayudarme a entender lo que quieres decir, por favor? @Barmar
– Aris Kantas
24 de noviembre de 2015 a las 2:22
Regla de oro: si duplica un extremo de una tubería a una entrada o salida estándar, debe cerrar ambos extremos de la tubería original antes de usar
exec*()
funciones (o continuar de otro modo). Hay excepciones; Son pocos y distantes entre sí. Es muy raro (en SO e IRL) que se encuentre con un programa que use conductos que cierren demasiados descriptores; es muy común encontrar un programa que no cierra suficientes descriptores. Es especialmente común si hay varios hijos y/o varias tuberías para confundir las cosas.–Jonathan Leffler
19 mayo 2017 a las 15:07
Por cierto, si miras el código en github.com/jleffler/soq/tree/master/src/so-4380-8114, encontrará un ejemplo interesante en el que ‘no cerrar suficientes descriptores de archivo hizo que el programa fallara en entradas lo suficientemente grandes’. Cuando la entrada era pequeña, funcionó bien. Cuando la entrada era lo suficientemente grande, el programa se atascaba porque las tuberías no estaban cerradas correctamente. (No, el código de interferencia no está directamente en Stack Overflow).
–Jonathan Leffler
19 mayo 2017 a las 15:22