2012-06-09 17 views
7

Estoy luchando por implementar un caparazón con tuberías para la clase.Tuberías UNIX en el bloque C en lectura

typedef struct { 
    char** cmd; 
    int in[2]; 
    int out[2]; 
} cmdio; 

cmdio cmds[MAX_PIPE + 1]; 

Comandos en la tubería se leen y se almacenan en cmds.

cmdio[i].in es el par de descriptores de archivo del tubo de entrada devuelto por pipe(). Para el primer comando, que se lee desde la entrada del terminal, es simplemente {fileno (stdin), -1}. cmdin[i].out es similar para la salida de salida de tubería/terminal. cmdio[i].in es lo mismo que cmd[i-1].out. Por ejemplo:

$ ls -l | sort | wc 

CMD: ls -l 
IN: 0 -1 
OUT: 3 4 

CMD: sort 
IN: 3 4 
OUT: 5 6 

CMD: wc 
IN: 5 6 
OUT: -1 1 

Pasamos cada comando a process_command, que hace una serie de cosas:

for (cmdi = 0; cmds[cmdi].cmd != NULL; cmdi++) { 
    process_command(&cmds[cmdi]); 
} 

Ahora, en el interior process_command:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout));  
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 
    execvp(cmd->cmd[0], cmd->cmd); 
    exit(-1); 
} 

El problema es que leyendo desde la bloques de tubería para siempre:

COMMAND $ ls | wc 
Created pipe, in: 5 out: 6 
Foreground pid: 9042, command: ls, Exited, info: 0 
[blocked running read() within wc] 

Si, en lugar de intercambiar el proceso con execvp, acabo de hacer esto:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout)); 
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 

    char buf[6]; 
    read(fileno(stdin), buf, 5); 
    buf[5] = '\0'; 

    printf("%s\n", buf); 
    exit(0); 
} 

Le pasa a trabajar:

COMMAND $ cmd1 | cmd2 | cmd3 | cmd4 | cmd5 
Pipe creada, in: 11 out: 12 
Pipe creada, in: 13 out: 14 
Pipe creada, in: 15 out: 16 
Pipe creada, in: 17 out: 18 
hola! 
Foreground pid: 9251, command: cmd1, Exited, info: 0 
Foreground pid: 9252, command: cmd2, Exited, info: 0 
Foreground pid: 9253, command: cmd3, Exited, info: 0 
Foreground pid: 9254, command: cmd4, Exited, info: 0 
hola! 
Foreground pid: 9255, command: cmd5, Exited, info: 0 

Cuál podría ser el problema?

Respuesta

4

No tiene suficientes cierres. En el código:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout));  
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 
    execvp(cmd->cmd[0], cmd->cmd); 
    exit(-1); 
} 

Después de haber duplican las tuberías para stdin y stdout (a través de fileno()), es necesario cerrar las tuberías:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout));  
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 
    close(cmd->in[0]); // Or your error checked version, but I'd use a function 
    close(cmd->out[1]);  
    execvp(cmd->cmd[0], cmd->cmd); 
    exit(-1); 
} 

Los programas no están terminando porque no hay un final de escritura del archivo aún abierto. Además, no olvide que si el proceso principal (shell) crea las tuberías, debe cerrar ambos extremos de la tubería. No cerrar las tuberías suficientes es probablemente el error más común cuando se empieza a aprender la plomería con tuberías.

+0

Hum, no funciona: \ Sigue siendo lo mismo, excepto que stdin/stdout se cierra antes de ejecutar el comando, por ejemplo 'cat' falla con' cat: stdin: Bad file descriptor'. Pero incluso si evito cerrar stdin/out, el comportamiento es el mismo que antes. Por lo que entiendo ambos 'cmd -> (in | out) [(0 | 1)]' se refieren a los mismos archivos subyacentes como 'fileno (std (in | out)', ¿no? –

+0

Bien Cada tubo tiene un descriptor de lectura y un descriptor de escritura. Según tengo entendido, tiene dos derivaciones entre el padre y el hijo (o entre los dos hijos).En cada proceso secundario, solo querrá un 'dup2()' del extremo de lectura de una tubería abierta en 'stdin' y solo' dup2() 'del extremo de escritura de la otra tubería abierta en' stdout'; los cuatro extremos originales de la tubería deberían estar cerrados (que es lo que sorprende a las personas). No estoy seguro si tenemos suficiente del código para decir qué más está yendo mal. –

+0

Gracias Jonathan, no estaba cerrando ambos extremos de los tubos del padre como dijiste. ¡Muchas gracias! –

1

Bien, finalmente lo resolví.

En el proceso principal, justo después de todo el tenedor niño, puse:

f (cmd->in[0] != fileno(stdin)) { 
    close(cmd->in[0]); 
    close(cmd->in[1]); 
} 

y listo. Ya había hecho algo así antes, pero lo escribí mal y lo hice close(cmd->out[0]);. Eso es todo. Tiene sentido ahora.