C Minishell Agregar tuberías

Así que estoy haciendo una minishell de UNIX, y estoy tratando de agregar tuberías, por lo que puedo hacer cosas como esta:

ps aux | grep dh | grep -v grep | cut -c1-5 

Sin embargo, tengo problemas para rodear la parte de la tubería. Reemplazo todo el “|” caracteres con 0, y luego ejecuta cada línea como una línea normal. Sin embargo, estoy tratando de desviar la salida y la entrada. La entrada de un comando debe ser la salida del comando anterior, y la salida de un comando debe ser la entrada del siguiente comando.

Estoy haciendo esto usando tuberías, sin embargo, no puedo averiguar dónde llamar a pipe () y dónde cerrarlas. Desde la función de procesamiento principal, processline (), tengo este código:

 if((pix = findUnquotChar(line_itr, '|'))) { line_itr[pix++] = 0; if(pipe (fd) < 0) perror("pipe"); processline(line_itr, inFD, fd[1], pl_flags); line_itr = &(line_itr[pix]); while((pix = findUnquotChar(line_itr, '|')) && pix < line_len) { line_itr[pix++] = 0; //? if(pipe (fd) < 0) perror("pipe"); processline(line_itr, fd[0], fd[1] pl_flags); line_itr = &(line_itr[pix]); //? close(fd[0]); //? close(fd[1]); } return; } 

Entonces, estoy recursivamente (el código de arriba está en línea de proceso) enviando los comandos entre “|” para ser procesado por línea de proceso. Puedes ver dónde comenté el código anterior, no estoy seguro de cómo hacerlo funcionar. El 2 ° y 3 ° parámetro de la línea de proceso son respectivamente inputFD y outputFD, por lo que necesito procesar un comando, escribir la salida en un conducto y llamar nuevamente a processline en el siguiente comando, sin embargo esta vez el resultado del comando anterior es el entrada. Esto simplemente no parece que pueda funcionar, porque cada vez que cierro fd [0] estoy perdiendo el resultado anterior. ¿Necesito dos tubos separados, con los que pueda dar saltitos de un lado a otro?

Solo estoy teniendo problemas para ver cómo es posible con una sola tubería, si necesitan información adicional, solo pregunten. Aquí está toda la función de línea de proceso en caso de que quiera echar un vistazo:

http://pastebin.com/YiEdaYdj

EDITAR: Si alguien tiene un ejemplo de un caparazón que implementa tuberías me gustaría un enlace a la fuente, no he podido encontrar uno en google hasta el momento.

EDIT2: Aquí hay un ejemplo de mi situación:

 echo a | echo b | echo c 

Así que primero llamaría al caparazón así:

 processline("echo a", 0, fd[1], flags); .... processline("echo b", fd[0], NOT_SURE_GOES_HERE[1], flags); .... processline("echo c", NOT_SURE_GOES_HERE[0], NOT_SURE_EITHER[1], flags); 

Cada uno de estos ocurre una vez por iteración, y como puede ver, no puedo imaginar qué pasar para los descriptores de archivo de entrada y los descriptores de archivo de salida para la iteración de segundo y tercero (y así sucesivamente).

Aquí hay un código moderadamente genérico pero simple para ejecutar tuberías, un progtwig al que llamo pipeline . Es un SSCCE en un único archivo como el presentado, aunque tendría los archivos stderr.h y stderr.c como archivos separados en una biblioteca para vincularlos con todos mis progtwigs. (En realidad, tengo un conjunto de funciones más complejo en mi stderr.c y stderr.h ‘reales’, pero este es un buen punto de partida).

El código opera de dos maneras. Si no proporciona argumentos, ejecuta una interconexión incorporada:

 who | awk '{print $1}' | sort | uniq -c | sort -n 

Esto cuenta el número de veces que cada persona ha iniciado sesión en el sistema, presentando la lista en orden creciente de sesiones. Alternativamente, puede invocar con una secuencia de argumentos que son la línea de comando que desea invocar, use un tubo entre comillas '|' (o "|" ) para separar los comandos:

Válido:

 pipeline pipeline ls '|' wc pipeline who '|' awk '{print $1}' '|' sort '|' uniq -c '|' sort -n pipeline ls 

Inválido:

 pipeline '|' wc -l pipeline ls '|' '|' wc -l pipeline ls '|' wc -l '|' 

Las últimas tres invocaciones imponen ‘tuberías como separadores’. El código no verifica el error de cada llamada al sistema; verifica el error fork() , execvp() y pipe() , pero omite comprobar en dup2() y close() . No incluye la impresión de diagnóstico para los comandos que se generan; una opción -x para la pipeline sería una adición sensata, haciendo que imprima un rastro de lo que hace. Tampoco sale con el estado de salida del último comando en la tubería.

Tenga en cuenta que el código comienza con un niño siendo bifurcado. El niño se convertirá en el último proceso en la tubería, pero primero crea una tubería y bifurca otro proceso para ejecutar los procesos anteriores en la tubería. Es poco probable que las funciones mutuamente recursivas sean la única forma de ordenar las cosas, pero dejan una mínima repetición del código (borradores anteriores del código tenían el contenido de exec_nth_command() repetido en gran medida en exec_pipeline() y exec_pipe_command() ).

La estructura del proceso aquí es tal que el proceso original solo conoce el último proceso en la tubería. Es posible rediseñar las cosas de tal manera que el proceso original sea el padre de cada proceso en la tubería, por lo que el proceso original puede informar por separado sobre el estado de cada comando en la tubería. Todavía no he modificado el código para permitir esa estructura; será un poco más complejo, aunque no tan horrible.

 /* One way to create a pipeline of N processes */ /* stderr.h */ #ifndef STDERR_H_INCLUDED #define STDERR_H_INCLUDED static void err_setarg0(const char *argv0); static void err_sysexit(char const *fmt, ...); static void err_syswarn(char const *fmt, ...); #endif /* STDERR_H_INCLUDED */ /* pipeline.c */ #include  #include  #include  #include  #include  /*#include "stderr.h"*/ typedef int Pipe[2]; /* exec_nth_command() and exec_pipe_command() are mutually recursive */ static void exec_pipe_command(int ncmds, char ***cmds, Pipe output); /* With the standard output plumbing sorted, execute Nth command */ static void exec_nth_command(int ncmds, char ***cmds) { assert(ncmds >= 1); if (ncmds > 1) { pid_t pid; Pipe input; if (pipe(input) != 0) err_sysexit("Failed to create pipe"); if ((pid = fork()) < 0) err_sysexit("Failed to fork"); if (pid == 0) { /* Child */ exec_pipe_command(ncmds-1, cmds, input); } /* Fix standard input to read end of pipe */ dup2(input[0], 0); close(input[0]); close(input[1]); } execvp(cmds[ncmds-1][0], cmds[ncmds-1]); err_sysexit("Failed to exec %s", cmds[ncmds-1][0]); /*NOTREACHED*/ } /* Given pipe, plumb it to standard output, then execute Nth command */ static void exec_pipe_command(int ncmds, char ***cmds, Pipe output) { assert(ncmds >= 1); /* Fix stdout to write end of pipe */ dup2(output[1], 1); close(output[0]); close(output[1]); exec_nth_command(ncmds, cmds); } /* Execute the N commands in the pipeline */ static void exec_pipeline(int ncmds, char ***cmds) { assert(ncmds >= 1); pid_t pid; if ((pid = fork()) < 0) err_syswarn("Failed to fork"); if (pid != 0) return; exec_nth_command(ncmds, cmds); } /* Collect dead children until there are none left */ static void corpse_collector(void) { pid_t parent = getpid(); pid_t corpse; int status; while ((corpse = waitpid(0, &status, 0)) != -1) { fprintf(stderr, "%d: child %d status 0x%.4X\n", (int)parent, (int)corpse, status); } } /* who | awk '{print $1}' | sort | uniq -c | sort -n */ static char *cmd0[] = { "who", 0 }; static char *cmd1[] = { "awk", "{print $1}", 0 }; static char *cmd2[] = { "sort", 0 }; static char *cmd3[] = { "uniq", "-c", 0 }; static char *cmd4[] = { "sort", "-n", 0 }; static char **cmds[] = { cmd0, cmd1, cmd2, cmd3, cmd4 }; static int ncmds = sizeof(cmds) / sizeof(cmds[0]); static void exec_arguments(int argc, char **argv) { /* Split the command line into sequences of arguments */ /* Break at pipe symbols as arguments on their own */ char **cmdv[argc/2]; // Way too many char *args[argc+1]; int cmdn = 0; int argn = 0; cmdv[cmdn++] = &args[argn]; for (int i = 1; i < argc; i++) { char *arg = argv[i]; if (strcmp(arg, "|") == 0) { if (i == 1) err_sysexit("Syntax error: pipe before any command"); if (args[argn-1] == 0) err_sysexit("Syntax error: two pipes with no command between"); arg = 0; } args[argn++] = arg; if (arg == 0) cmdv[cmdn++] = &args[argn]; } if (args[argn-1] == 0) err_sysexit("Syntax error: pipe with no command following"); args[argn] = 0; exec_pipeline(cmdn, cmdv); } int main(int argc, char **argv) { err_setarg0(argv[0]); if (argc == 1) { /* Run the built in pipe-line */ exec_pipeline(ncmds, cmds); } else { /* Run command line specified by user */ exec_arguments(argc, argv); } corpse_collector(); return(0); } /* stderr.c */ /*#include "stderr.h"*/ #include  #include  #include  #include  #include  static const char *arg0 = ""; static void err_setarg0(const char *argv0) { arg0 = argv0; } static void err_vsyswarn(char const *fmt, va_list args) { int errnum = errno; fprintf(stderr, "%s:%d: ", arg0, (int)getpid()); vfprintf(stderr, fmt, args); if (errnum != 0) fprintf(stderr, " (%d: %s)", errnum, strerror(errnum)); putc('\n', stderr); } static void err_syswarn(char const *fmt, ...) { va_list args; va_start(args, fmt); err_vsyswarn(fmt, args); va_end(args); } static void err_sysexit(char const *fmt, ...) { va_list args; va_start(args, fmt); err_vsyswarn(fmt, args); va_end(args); exit(1); } 

Señales y SIGCHLD

La sección Conceptos de señal POSIX trata sobre SIGCHLD:

Debajo de SIG_DFL:

Si la acción predeterminada es ignorar la señal, la entrega de la señal no tendrá ningún efecto en el proceso.

En SIG_IGN:

Si la acción para la señal SIGCHLD se establece en SIG_IGN, los procesos hijos de los procesos llamantes no se transformarán en procesos zombies cuando finalicen. Si el proceso de llamada posteriormente espera a sus hijos, y el proceso no tiene hijos no esperados que se transformaron en procesos zombies, se bloqueará hasta que todos sus hijos finalicen, y wait () , waitid () y waitpid () deberán fallar y establecer errno a [ECHILD] .

La descripción de tiene una tabla de disposiciones predeterminadas para señales, y para SIGCHLD, el valor predeterminado es I (SIG_IGN).


Agregué otra función al código de arriba:

 #include  typedef void (*SigHandler)(int signum); static void sigchld_status(void) { const char *handling = "Handler"; SigHandler sigchld = signal(SIGCHLD, SIG_IGN); signal(SIGCHLD, sigchld); if (sigchld == SIG_IGN) handling = "Ignored"; else if (sigchld == SIG_DFL) handling = "Default"; printf("SIGCHLD set to %s\n", handling); } 

Lo llamé inmediatamente después de la llamada a err_setarg0() , e informa ‘Predeterminado’ en Mac OS X 10.7.5 y Linux (RHEL 5, x86 / 64). Validé su funcionamiento ejecutando:

 (trap '' CHLD; pipeline) 

En ambas plataformas, eso informó ‘Ignorado’, y el comando de pipeline ya no informó el estado de salida del niño; no lo entendió

Entonces, si el progtwig está ignorando SIGCHLD, no genera ningún zombi, pero espera hasta que “todos” sus hijos finalicen. Es decir, hasta que todos sus hijos directos terminan; un proceso no puede esperar a sus nietos o una progenie más distante, ni a sus hermanos, ni a sus antepasados.

Por otro lado, si la configuración de SIGCHLD es la predeterminada, la señal se ignora y se crean zombies.

Ese es el comportamiento más conveniente para este progtwig tal como está escrito. La función corpse_collector() tiene un bucle que recostack la información de estado de cualquier elemento corpse_collector() . Solo hay un niño a la vez con este código; el rest de la tubería se ejecuta como un niño (del niño, del niño, …) del último proceso en la tubería.


Sin embargo, estoy teniendo problemas con zombies / cadáveres. Mi profesor me hizo implementarlo de la misma manera que tú, ya que cmd1 no es el padre de cmd2 en el caso de: ” cmd1 | cmd2 | cmd3 “. A menos que le diga a mi shell que espere en cada proceso ( cmd1 , cmd2 y cmd3 ), en lugar de simplemente esperar en el último proceso ( cmd3 ), toda la tubería se apaga antes de que la salida pueda llegar al final. Estoy teniendo problemas para encontrar una buena manera de esperarlos; mi maestra dijo que usara WNOHANG.

No estoy seguro de entender el problema. Con el código que proporcioné, cmd3 es el padre de cmd2 , y cmd2 es el padre de cmd1 en una canalización de 3 comandos (y el shell es el padre de cmd3 ), por lo que el shell solo puede esperar en cmd3 . Lo dije originalmente:

La estructura del proceso aquí es tal que el proceso original solo conoce el último proceso en la tubería. Es posible rediseñar las cosas de tal manera que el proceso original sea el padre de cada proceso en la tubería, por lo que el proceso original puede informar por separado sobre el estado de cada comando en la tubería. Todavía no he modificado el código para permitir esa estructura; será un poco más complejo, aunque no tan horrible.

Si tiene su caparazón capaz de esperar en los tres comandos en la tubería, debe estar utilizando la organización alternativa.

La descripción de waitpid() incluye:

El argumento pid especifica un conjunto de procesos secundarios para los cuales se solicita el estado. La función waitpid () solo devolverá el estado de un proceso hijo de este conjunto:

  • Si pid es igual a (pid_t) -1, se solicita el estado para cualquier proceso hijo. En este sentido, waitpid () es equivalente a wait ().

  • Si pid es mayor que 0, especifica el ID del proceso de un único hijo para el que se solicita el estado.

  • Si pid es 0, se solicita el estado para cualquier proceso secundario cuyo ID de grupo de proceso sea igual al del proceso de llamada.

  • Si pid es menor que (pid_t) -1, se solicita el estado para cualquier proceso secundario cuyo ID de grupo de proceso sea igual al valor absoluto de pid.

El argumento de opciones se construye a partir del OR de bit a bit de cero o más de los siguientes indicadores, definidos en el encabezado:

WNOHANG La función waitpid() no suspenderá la ejecución de la waitpid() de llamada si el estado no está inmediatamente disponible para uno de los procesos hijo especificado por pid.

Esto significa que si está utilizando grupos de procesos y el shell sabe en qué grupo de procesos se está ejecutando el pipeline (por ejemplo, porque el pipeline se coloca en su propio grupo de procesos por el primer proceso), el padre puede esperar el apropiado niños para terminar.

… divagando … creo que hay información útil aquí; probablemente debería haber más de lo que estoy escribiendo, pero mi mente se ha quedado en blanco.