2010-08-19 23 views
6

Premisa: Escriba un programa para consultar al usuario para dos cadenas de entrada. Cada cadena de entrada debe ser un comando Unix, con argumentos permitidos. Por ejemplo, la entrada 1 podría ser ls -l y la entrada 2 podría ser more. El programa creará una tubería y dos procesos secundarios. El primer proceso secundario ejecutará el comando especificado en la primera entrada. Saldrá a la tubería en lugar de la salida estándar. El segundo proceso secundario ejecutará el comando especificado en la segunda entrada. Tomará su entrada de la tubería en lugar de la entrada estándar. El proceso principal esperará a que completen sus dos hijos, y luego todo se repetirá. La ejecución se detendrá cuando se ingrese el símbolo '@' como primer comando. Aquí está el código que tengo:Tuberías y procesos

#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 

int main(){ 

    /* Program Termination Symbol */ 
    const char terminate = '@'; 

    /* String delimiter */ 
    const char delimiter = ' '; 

    /* Pipe file ID's */ 
    int fileID[2]; 

    /* Parent ID's */ 
    int pid1, pid2; 

    /* String token */ 
    char * token, * token2; 

    /* User input */ 
    char * user_input, line[100]; 

    user_input = (char *) malloc(100); 

    /* Unix Commands */ 
    char * command1[10], *command2[10]; 

    for (int i=0; i<10; i++) 
    { 
    command1[i] = (char *)malloc(100*sizeof(char)); 
    command2[i] = (char *)malloc(100*sizeof(char)); 
    } 

    /* Begin main program logic */ 

    printf("Please enter the first command: \n"); 

    user_input = gets(line); 

    while (user_input[0] != terminate) 
    { 
    token = (char *) malloc(100*sizeof(char)); 

    for (int i=0; i<10; i++) 
     { 
     if (i == 0) 
     { 
     token = strtok(user_input, &delimiter); 
     } else { 
     token = strtok(NULL, &delimiter); 
     } 

     if (token != NULL) 
     { 
     strcpy(command1[i], token); 
     } else { 
     command1[i] = 0; 
     } 
     } 

    printf("Please enter the second command: \n"); 
    user_input = gets(line); 

    token2 = (char *) malloc(100*sizeof(char)); 

    for (int i=0; i<10; i++) 
    { 
     if (i == 0) 
     { 
     token2 = strtok(user_input, &delimiter); 
     } else { 
     token2 = strtok(NULL, &delimiter); 
     } 

     if (token2 != NULL) 
     { 
     strcpy(command2[i], token2); 
     } else { 
     command2[i] = 0; 
     } 
    } 


    /* Pipe and execute user commands */ 

    /* Create pipe */ 
    pipe(fileID); 

    /* Create child processes */ 

    pid1 = fork(); 

    if (pid1 != 0) 
    { 
     pid2 = fork(); 
    } 

    /* First child process */ 
    if (pid1 == 0) 
    { 
     dup2(fileID[1], 1); 
     execvp(command1[0], command1); 
    } 

    /* Second child process */ 
    if (pid2 == 0) 
    { 
     dup2(fileID[0], 0); 
     execvp(command2[0], command2); 
    } 

    /* Wait for children to terminate */ 
    wait(&pid1); 
    wait(&pid2); 

    /* Repeat */ 
     printf("Please enter the first command: \n"); 
    user_input = gets(line); 
    } 

    return 0; 
} 

El problema que me encuentro es con mi espera. Si tengo ambas cosas, lo cual tendría sentido para mí (una espera por niño), entonces el programa se congela después de ejecutar la primera tubería. Si elimino la segunda espera, el programa comenzará de nuevo su ciclo, pero no aceptará la entrada del teclado que no sea enter, y producirá una segfault. Entonces, con ambas esperas, la entrada y la salida es ...

Please enter the first command: 
ls 
Please enter the second command: 
more 
Pipe 
Pipe.c 
Pipe.c~ 

... y luego se bloquea. Si elimino la segunda espera, la entrada/salida es ...

Please enter the first command: 
ls 
Please enter the second command: 
more 
Pipe 
Pipe.c 
Pipe.c~ 
Please enter the first command: 
(I hit enter, nothing else will work) 
Segmentation fault 

¿Alguien tiene alguna sugerencia? Está claramente relacionado con esperar en los dos procesos, pero no sé cómo manejarlo.


Este programa ahora es 100% funcional, ¡muchas gracias por su ayuda, a todos! El desbordamiento de la pila ha sido uno de los mejores recursos en Internet. Muchas gracias a todos por tomarse el tiempo para revisar mi código y darme sus sugerencias.

+0

Solo tiene que esperar al niño al final de la tubería. –

+0

@PeterRitchie Tenga en cuenta que la etiqueta de tarea ha quedado obsoleta (lea la descripción). Gracias. – Tim

Respuesta

5

Estoy de acuerdo con todo lo que torak dijo, pero para solucionar su problema, debe cerrar sus tuberías. Creo que estás "colgando" porque la tubería todavía está abierta.

Entonces en el padre, justo antes de la "espera", cerraría las tuberías.

close(fileID[0]); 
close(fileID[1]); 
wait(&pid_status); 
wait(&pid_status); 

Entonces, justo antes de cada execvp, me gustaría cerrar los extremos de la tubería el niño no va a utilizar:

close(fileID[0]); 
dup2(fileID[1], 1); 
execvp(command1[0], command1); 


close(fileID[1]); 
dup2(fileID[0], 0); 
execvp(command2[0], command2); 

Esto debería resolver su colgante. Además de las sugerencias hechas por torak, también recomendaría fgets en lugar de evitar un desbordamiento de búfer.

+0

Eso resolvió el ahorcamiento, muchas gracias. El programa ahora funciona (principalmente) correctamente. Esta asignación me ha estado volviendo loco. Pasamos la última semana discutiendo sobre la gestión de la memoria, así que estaba convencido de que nuestra asignación de programación iba a cubrir eso ... pero, no, desde fuera del campo izquierdo, las tuberías. – rybosome

4

Un par de cosas. No estoy seguro de que sean la causa de sus problemas, pero aún hay cosas que considerar antes de enviar su tarea.

  1. No creo que esté utilizando wait correctamente. De acuerdo con http://linux.die.net/man/2/wait, no toma el puntero pid como argumento.

  2. Cada vez que recorre el ciclo, llama a malloc para token y token2, pero no veo una versión correspondiente de la memoria.

  3. Has escrito una sola función monolítica. Las buenas prácticas de codificación sugeriría romperlo a cabo en una colección de subrutinas

  4. Por último, y su posible relación con el punto 3, las siguientes dos líneas de código aparecen dos veces en el código. De nuevo, no es un error, sino una duplicación innecesaria y poco elegante.

    printf ("Introduzca el primer comando: \ n"); user_input = obtiene (línea);

+0

Sí, la espera toma un estado llamado int *. Considere usar waitpid, que toma pid, status y options como argumentos. – Robb

+0

Probablemente ya debería saberlo, pero ¿alguien me puede explicar por qué el código al final de mi respuesta no está saliendo bien? – torak

+0

¿Recordó sangrar la línea? –

1

En primer lugar, que está llamando wait de los procesos secundarios, así [editar: no, no es así, ya que cada niño llama execvp].

Además, wait no toma un puntero al pid del niño, sino a una variable donde se escribirá el estado del proceso (lo que significa que está tirando el pid de su hijo).

Finalmente, intente usar waitpid con la opción "WNOHANG". No se bloqueará, puede poner ambos en un bucle mientras hace otras cosas, y puede verificar si los procesos secundarios han salido inspeccionando las variables de estado. man waitpid.

+0

Creo que en realidad no estoy llamando a la espera de los procesos hijos: cuando se encuentra la instrucción execvp, el resto del código en el proceso actual no se ejecuta. Se reemplaza con el código de la llamada execvp. Al menos, esa es mi comprensión de eso. En cuanto al uso del pid como valor pasado ... No me preocupa el estado que se devuelve de la espera, por lo que le paso un valor que ya no necesito. Según entiendo, esperar simplemente esperará en un proceso hijo único; no hay forma de controlar cuál con esa función. – rybosome

+0

Es cierto, execvp detiene la ejecución de los hijos. Redactaré mi respuesta. En cuanto al pid, puede que no lo necesite ahora, pero es * muy * mala forma de reutilizar variables para diferentes propósitos como este, y eventualmente puede cambiar su programa de una manera que necesite el pid más tarde. Entonces, especialmente para una tarea asignada, debe usar diferentes variables para el estado. No tiene sentido tratar de ser frugal con nombres variables. Y la función waitpid le permite definir un pid específico para esperar. Eche un vistazo a su página de manual. – rbp

+0

Tienes razón en que esta es una mala práctica. Debería hacer otra variable. En estos días, no es que una sola variable int sea un espacio terriblemente necesario que debe conservarse a toda costa. Cambié mi programa para hacer esto. Aprecio tu sugerencia! – rybosome

Cuestiones relacionadas