Frage

Hier ist mein Code:

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

(Der Code wurde aktualisiert er, um Änderungen von zwei Antworten vorgeschlagen unten, ist es noch nicht, wie es sollte funktioniert ...)

Hier ist der Testfall, wenn dies fehlschlägt:

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

Das Problem ist, dass ich meine Shell danach zurückkehren sollte, sollte ich sehen „Shell>“ für weitere Eingabe wartet. Sie können auch feststellen, dass Sie keine Meldung ähnlich „[4804] BEENDET (Status: 0)“ sehen. (Aber mit einem anderen pid), die den zweiten Prozess bedeutet nicht beenden

Ich denke, es hat etwas mit grep zu tun, denn das funktioniert:

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)

Sie können ganz einfach zwei "beenden" Nachrichten sehen ...

Also, was ist los mit meinem Code?

War es hilfreich?

Lösung

Auch nach dem ersten Befehl des Pipeline-Exits (und Thust schließt stdout=~fdPipe[1]) haben die Eltern noch fdPipe[1] offen.

So hat der zweite Befehl der Pipeline eine stdin=~fdPipe[0], die nie ein EOF bekommt, weil der andere Endpunkt des Rohres noch offen ist.

Sie müssen eine neue pipe(fdPipe) für jeden | erstellen, und stellen Sie sicher, beiden Endpunkte in den Eltern zu schließen; d.

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

Auch als sicherer, sollten Sie den Fall von fdPipe und {STDIN_FILENO,STDOUT_FILENO} überlappende behandeln, bevor eine der close und dup2 Operationen. Dies kann passieren, wenn jemand Ihre Schale mit stdin oder stdout geschlossen zu starten geschafft hat, und hier in großer Verwirrung mit dem Code führen wird.

Bearbeiten

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

Neben dafür, dass Sie das Rohr Endpunkte in der Mutter schließen, ich hatte versucht, den Punkt zu machen, die fdPipe1, fdPipe2 usw. nicht die gleiche seine 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);

Ich weiß, dass Sie nicht close(0) in Ihrer aktuellen Code nicht verwenden, aber der letzte Absatz warnt Sie für diesen Fall zu achten.

Bearbeiten

Das folgende minimal Änderung an Ihrem Code macht es im konkreten Fall andernfalls arbeiten Sie erwähnt:

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

Dies wird nicht für mehr als einen Befehl in der Pipeline arbeiten; für dass, würden Sie so etwas wie dies benötigen: (ungetestet, wie Sie auch andere Dinge zu beheben haben)

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

Andere Tipps

Sie sollten einen Fehler Ausfahrt nach execvp haben (.) - es wird irgendwann scheitern

exit(EXIT_FAILURE);

Wie @uncleo weist darauf hin, muss die Argumentliste hat einen Null-Zeiger das Ende, um anzuzeigen:

cmdArgs[aCount] = 0;

Es ist mir nicht klar, dass Sie beide Programme frei laufen lassen - es scheint, dass Sie das erste Programm in der Pipeline erfordern vor dem Start des zweiten zu beenden, die, wenn die ersten Programmblöcke, weil das Rohr kein Rezept für Erfolg voll ist.

Jonathan hat die richtige Idee. Sie stützen sich auf den ersten Prozess alle anderen gabeln. Jeder hat zu Ende laufen, bevor die nächsten gegabelt ist.

Stattdessen gabeln die Prozesse in einer Schleife wie Sie tun, aber für sie außerhalb der inneren Schleife warten, (am unteren Rand der großen Schleife für die Shell-Eingabeaufforderung).

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

Ich denke, Ihre gegabelt Prozesse Ausführung wird fortgesetzt.

Versuchen Sie entweder:

  • Ändern Sie ihn auf 'Rückkehr execvp'
  • Add 'exit (1);' nach execvp

Ein mögliches Problem ist, dass cmdargs Müll am Ende davon haben kann. Sie sollten das Array mit einem Null-Zeiger beenden, bevor er es execvp ().

Es sieht aus wie grep ist STDIN zu akzeptieren, obwohl, so dass möglicherweise keine Probleme verursacht (noch) nicht.

Die Datei-Deskriptoren von dem Rohr sind Referenz gezählt und mit jeder Gabel inkrementiert. für jede Gabel, haben Sie eine enge auf beiden Deskriptoren auszugeben, um den Referenzzähler auf Null und damit das Rohr zu schließen, zu reduzieren. Ich nehme an.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top