Pregunta

Aquí está mi código:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <readline/readline.h>

#define NUMPIPES 2

int main(int argc, char *argv[]) {
    char *bBuffer, *sPtr, *aPtr = NULL, *pipeComms[NUMPIPES], *cmdArgs[10];
    int fdPipe[2], pCount, aCount, i, status, lPids[NUMPIPES];
    pid_t pid;

    pipe(fdPipe);

    while(1) {
        bBuffer = readline("Shell> ");

        if(!strcasecmp(bBuffer, "exit")) {
            return 0;
        }

        sPtr = bBuffer;
        pCount = -1;

        do {
            aPtr = strsep(&sPtr, "|");
            pipeComms[++pCount] = aPtr;
        } while(aPtr);

        for(i = 0; i < pCount; i++) {
            aCount = -1;

            do {
                aPtr = strsep(&pipeComms[i], " ");
                cmdArgs[++aCount] = aPtr;
            } while(aPtr);

            cmdArgs[aCount] = 0;

            if(strlen(cmdArgs[0]) > 0) {
                pid = fork();

                if(pid == 0) {
                    if(i == 0) {
                        close(fdPipe[0]);

                        dup2(fdPipe[1], STDOUT_FILENO);

                        close(fdPipe[1]);
                    } else if(i == 1) {
                        close(fdPipe[1]);

                        dup2(fdPipe[0], STDIN_FILENO);

                        close(fdPipe[0]);
                    }

                    execvp(cmdArgs[0], cmdArgs);
                    exit(1);
                } else {
                    lPids[i] = pid;

                    /*waitpid(pid, &status, 0);

                    if(WIFEXITED(status)) {
                        printf("[%d] TERMINATED (Status: %d)\n",
                            pid, WEXITSTATUS(status));
                    }*/
                }
            }
        }

        for(i = 0; i < pCount; i++) {
            waitpid(lPids[i], &status, 0);

            if(WIFEXITED(status)) {
                printf("[%d] TERMINATED (Status: %d)\n",
                    lPids[i], WEXITSTATUS(status));
            }
        }
    }

    return 0;
}

(El código se ha actualizado para reflejar que los cambios propuestos por dos respuestas a continuación, todavía no funciona como debería ...)

Este es el caso de prueba, donde esto no funciona:

nazgulled ~/Projects/SO/G08 $ ls -l
total 8
-rwxr-xr-x 1 nazgulled nazgulled  7181 2009-05-27 17:44 a.out
-rwxr-xr-x 1 nazgulled nazgulled   754 2009-05-27 01:42 data.h
-rwxr-xr-x 1 nazgulled nazgulled  1305 2009-05-27 17:50 main.c
-rwxr-xr-x 1 nazgulled nazgulled   320 2009-05-27 01:42 makefile
-rwxr-xr-x 1 nazgulled nazgulled 14408 2009-05-27 17:21 prog
-rwxr-xr-x 1 nazgulled nazgulled  9276 2009-05-27 17:21 prog.c
-rwxr-xr-x 1 nazgulled nazgulled 10496 2009-05-27 17:21 prog.o
-rwxr-xr-x 1 nazgulled nazgulled    16 2009-05-27 17:19 test
nazgulled ~/Projects/SO/G08 $ ./a.out 
Shell> ls -l|grep prog
[4804] TERMINATED (Status: 0)
-rwxr-xr-x 1 nazgulled nazgulled 14408 2009-05-27 17:21 prog
-rwxr-xr-x 1 nazgulled nazgulled  9276 2009-05-27 17:21 prog.c
-rwxr-xr-x 1 nazgulled nazgulled 10496 2009-05-27 17:21 prog.o

El problema es que debo volver a mi concha después de eso, debería ver "Shell>" a la espera de más de entrada. También se puede notar que usted no ve un mensaje similar a "[4804] TERMINADO (estado: 0)". (Pero con un PID diferente), lo que significa que el segundo proceso no terminó

Creo que tiene algo que ver con grep, porque esto funciona:

nazgulled ~/Projects/SO/G08 $ ./a.out 
Shell> echo q|sudo fdisk /dev/sda
[4838] TERMINATED (Status: 0)

The number of cylinders for this disk is set to 1305.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
   (e.g., DOS FDISK, OS/2 FDISK)

Command (m for help): 
[4839] TERMINATED (Status: 0)

Se puede ver fácilmente dos mensajes de "terminar" ...

Entonces, ¿qué hay de malo en mi código?

¿Fue útil?

Solución

Incluso después de que el primer comando de las salidas de tuberías (y Thust cierra stdout=~fdPipe[1]), el padre todavía tiene fdPipe[1] abierta.

Por lo tanto, el segundo comando de la tubería tiene una stdin=~fdPipe[0] que nunca pasa de un EOF, porque el otro extremo de la tubería está todavía abierto.

Es necesario crear un nuevo pipe(fdPipe) para cada |, y asegúrese de cerrar ambos extremos de la matriz; es decir.

for cmd in cmds
    if there is a next cmd
        pipe(new_fds)
    fork
    if child
        if there is a previous cmd
            dup2(old_fds[0], 0)
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            close(new_fds[0])
            dup2(new_fds[1], 1)
            close(new_fds[1])
        exec cmd || die
    else
        if there is a previous cmd
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            old_fds = new_fds
if there are multiple cmds
    close(old_fds[0])
    close(old_fds[1])

Además, para ser más seguro, usted debe manejar el caso de fdPipe y {STDIN_FILENO,STDOUT_FILENO} superposición antes de realizar cualquiera de las operaciones close y dup2. Esto puede suceder si alguien ha conseguido iniciar su cáscara con la entrada estándar o la salida estándar cerrado, y dará lugar a una gran confusión con el código aquí.

Editar

   fdPipe1           fdPipe3
      v                 v
cmd1  |  cmd2  |  cmd3  |  cmd4  |  cmd5
               ^                 ^
            fdPipe2           fdPipe4

Además de asegurarse de cerrar los extremos de la tubería en el padre, que estaba tratando de hacer el punto de que fdPipe1, fdPipe2, etc. no pueden ser el mismo pipe().

/* suppose stdin and stdout have been closed...
 * for example, if your program was started with "./a.out <&- >&-" */
close(0), close(1);

/* then the result you get back from pipe() is {0, 1} or {1, 0}, since
 * fd numbers are always allocated from the lowest available */
pipe(fdPipe);

close(0);
dup2(fdPipe[0], 0);

Sé que no utiliza close(0) en el código actual, pero el último párrafo está advirtiendo que usted pueda ver a cabo para este caso.

Editar

La siguiente mínima cambio en su código hace que funcione en el caso falla específica que usted ha mencionado:

@@ -12,6 +12,4 @@
     pid_t pid;

-    pipe(fdPipe);
-
     while(1) {
         bBuffer = readline("Shell> ");
@@ -29,4 +27,6 @@
         } while(aPtr);

+        pipe(fdPipe);
+
         for(i = 0; i < pCount; i++) {
                 aCount = -1;
@@ -72,4 +72,7 @@
         }

+        close(fdPipe[0]);
+        close(fdPipe[1]);
+
         for(i = 0; i < pCount; i++) {
                 waitpid(lPids[i], &status, 0);

Esto no funcionará durante más de un comando en la canalización; para ello, se necesitaría algo como esto: (no probado, ya que hay que solucionar otras cosas también)

@@ -9,9 +9,7 @@
 int main(int argc, char *argv[]) {
     char *bBuffer, *sPtr, *aPtr = NULL, *pipeComms[NUMPIPES], *cmdArgs[10];
-    int fdPipe[2], pCount, aCount, i, status, lPids[NUMPIPES];
+    int fdPipe[2], fdPipe2[2], pCount, aCount, i, status, lPids[NUMPIPES];
     pid_t pid;

-    pipe(fdPipe);
-
     while(1) {
         bBuffer = readline("Shell> ");
@@ -32,4 +30,7 @@
                 aCount = -1;

+                if (i + 1 < pCount)
+                    pipe(fdPipe2);
+
                 do {
                         aPtr = strsep(&pipeComms[i], " ");
@@ -43,11 +44,12 @@

                         if(pid == 0) {
-                                if(i == 0) {
-                                        close(fdPipe[0]);
+                                if(i + 1 < pCount) {
+                                        close(fdPipe2[0]);

-                                        dup2(fdPipe[1], STDOUT_FILENO);
+                                        dup2(fdPipe2[1], STDOUT_FILENO);

-                                        close(fdPipe[1]);
-                                } else if(i == 1) {
+                                        close(fdPipe2[1]);
+                                }
+                                if(i != 0) {
                                         close(fdPipe[1]);

@@ -70,4 +72,17 @@
                         }
                 }
+
+                if (i != 0) {
+                    close(fdPipe[0]);
+                    close(fdPipe[1]);
+                }
+
+                fdPipe[0] = fdPipe2[0];
+                fdPipe[1] = fdPipe2[1];
+        }
+
+        if (pCount) {
+            close(fdPipe[0]);
+            close(fdPipe[1]);
         }

Otros consejos

Usted debe tener una salida de error después de execvp () -. Fallará en algún momento

exit(EXIT_FAILURE);

Como @uncleo señala, la lista de argumentos debe tener un puntero nulo para indicar el final:

cmdArgs[aCount] = 0;

No está claro para mí que deja que ambos programas se ejecutan gratis - parece que se necesita el primer programa en la tubería para terminar antes de comenzar la segunda, que no es una receta para el éxito si los primeros bloques de programa porque el tubo está lleno.

Jonathan tiene la idea correcta. Que se basan en el primer proceso que desembolsar todos los demás. Cada uno tiene que correr hasta el final antes de que se bifurca la siguiente.

En lugar de ello, los procesos de tenedor en un circuito como el que está haciendo, pero esperar a que fuera del bucle interno, (en la parte inferior de la gran lazo para el intérprete de comandos).

loop //for prompt
    next prompt
    loop //to fork tasks, store the pids
        if pid == 0 run command
        else store the pid
    end loop
    loop // on pids
        wait
    end loop
end loop

Creo que sus procesos bifurcados continuarán ejecutando.

Trate bien:

  • Si lo cambia a 'retorno execvp'
  • Añadir 'de salida (1);' después de execvp

Un problema potencial es que cmdargs pueden tener la basura al final de la misma. Se supone que debes poner fin a esa matriz con un puntero nulo antes de pasarla a execvp ().

Parece que grep está aceptando STDIN, aunque, por lo que podrían no causa ningún problema (aún).

los descriptores de archivos de la tubería se cuentan de referencia, y incrementa con cada tenedor. para cada tenedor, lo que tiene que emitir un cierre en ambos descriptores con el fin de reducir el número de referencia a cero y permitir que la tubería para cerrar. Supongo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top