Domanda

Ecco il mio codice:

#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;
}

(Il codice è stato aggiornato per riflettere che le modifiche proposte da due risposte qui sotto, che ancora non funziona come dovrebbe ...)

Ecco il banco di prova in cui questo non riesce:

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

Il problema è che io tornassi al mio shell dopo che, dovrei vedere "Shell>" in attesa di più input. Si può anche notare che non si vede un messaggio simile a "[4804] TERMINATO (stato: 0)". (Ma con un diverso PID), il che significa che il secondo processo non terminerà

Credo che abbia qualcosa a che fare con grep, perché questo funziona:

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)

Si può facilmente vedere due messaggi "terminare" ...

Quindi, cosa c'è di sbagliato con il mio codice?

È stato utile?

Soluzione

Anche dopo il primo comando delle vostre uscite di tubazioni (e Thust chiude stdout=~fdPipe[1]), il genitore ha ancora fdPipe[1] aperta.

Così, il secondo comando della tubazione ha un stdin=~fdPipe[0] che non viene mai un EOF, perché l'altro estremo del tubo è ancora aperto.

È necessario creare un nuovo pipe(fdPipe) per ogni |, e assicurarsi di chiudere entrambi gli endpoint nel genitore; cioè.

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])

Inoltre, per essere più sicuro, si dovrebbe gestire il caso di fdPipe e {STDIN_FILENO,STDOUT_FILENO} sovrapposizione prima di eseguire una qualsiasi delle operazioni close e dup2. Ciò può accadere se qualcuno è riuscito ad avviare la shell con stdin o stdout chiuse, e si tradurrà in una grande confusione con il codice qui.

Modifica

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

Oltre a fare in modo di chiudere gli endpoint del tubo nel genitore, stavo cercando di fare il punto che fdPipe1, fdPipe2, ecc non può essere lo stesso 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);

Lo so che non si utilizza close(0) nel codice attuale, ma l'ultimo paragrafo è allarme di guardare fuori per questo caso.

Modifica

Il seguente minima modifica al codice lo fa funzionare, nel caso specifico difetto lei ha citato:

@@ -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);

Questo non funziona per più di un comando nella pipeline; per questo, avresti bisogno di qualcosa di simile: (non testato, come si deve risolvere anche altre cose)

@@ -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]);
         }

Altri suggerimenti

Si dovrebbe avere un'uscita di errore dopo execvp () -. Fallirà qualche tempo

exit(EXIT_FAILURE);

Come @uncleo sottolinea, la lista degli argomenti deve avere un puntatore nullo per indicare la fine:

cmdArgs[aCount] = 0;

Non è chiaro per me che si lascia che entrambi i programmi correre liberi - sembra che si richiede il primo programma in cantiere per finire prima di iniziare la seconda, che non è una ricetta per il successo se i primi blocchi di programma perché il tubo è pieno.

Jonathan ha l'idea giusta. Ci si affida il primo processo di sborsare tutti gli altri. Ognuno deve correre a compimento prima della successiva è biforcuta.

Invece, sborsare i processi in un ciclo, come si sta facendo, ma attendere per loro al di fuori del ciclo interno, (in fondo il grande anello per il prompt della shell).

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

Credo che i processi biforcuta continuerà l'esecuzione.

Prova uno:

  • Cambiare a 'execvp ritorno'
  • Aggiungi 'exit (1);' dopo execvp

Un potenziale problema è che cmdargs possono avere spazzatura, alla fine di esso. Dovresti porre termine a tale matrice con un puntatore nullo prima di passarlo a execvp ().

Sembra che grep sta accettando STDIN, però, in modo che non potrebbe essere la causa dei problemi (ancora).

i descrittori di file dal tubo vengono contati riferimento, e incrementate con ogni forcella. per ogni forcella, è necessario emettere una stretta su entrambi i descrittori per ridurre il conteggio di riferimento a zero e consentire al tubo di chiudere. Sto indovinando.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top