Question

Voici mon 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;
}

(Le code a été mis à jour pour refléter les changements proposés par il deux réponses ci-dessous, il ne fonctionne toujours pas comme il se doit ...)

Voici le cas de test où cela échoue:

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

Le problème est que je revenir à ma coquille après cela, je devrais voir « Shell> » en attente pour plus d'entrée. Vous pouvez également remarquer que vous ne voyez pas un message similaire à « [4804] TERMINÉ (Statut: 0) ». (Mais avec un autre pid), ce qui signifie que le second processus n'a pas mis fin

Je pense qu'il a quelque chose à voir avec grep, parce que cela fonctionne:

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)

Vous pouvez facilement voir deux messages "terminate" ...

Alors, quel est le problème avec mon code?

Était-ce utile?

La solution

Même après la première commande de vos sorties de pipeline (et Thust ferme stdout=~fdPipe[1]), le

parent a toujours fdPipe[1] ouvert.

Ainsi, la deuxième commande du pipeline a une stdin=~fdPipe[0] qui ne reçoit un EOF, parce que l'autre extrémité de la conduite est toujours ouverte.

Vous devez créer une nouvelle pipe(fdPipe) pour chaque |, et assurez-vous de fermer les deux extrémités du parent; i.e..

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

En outre, pour être plus sûr, vous devez gérer le cas de chevauchement de fdPipe et {STDIN_FILENO,STDOUT_FILENO} avant d'effectuer l'une des opérations de close et dup2. Cela peut se produire si quelqu'un a réussi à démarrer votre shell avec stdin ou stdout fermé, et se traduira par une grande confusion avec le code ici.

Modifier

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

En plus de vous que vous fermez les points d'extrémité du tuyau dans le parent, je tentais de faire valoir que fdPipe1, fdPipe2, etc. ne peuvent pas la même 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);

Je sais que vous n'utilisez pas close(0) dans votre code actuel, mais le dernier paragraphe vous avertit de regarder pour ce cas.

Modifier

les minimum changement à votre code fait fonctionner dans le cas particulier d'échec que vous avez mentionné:

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

Cela ne fonctionnera pas pendant plus d'une commande dans le pipeline; pour cela, vous avez besoin de quelque chose comme ceci: (non testé, comme vous devez fixer d'autres choses aussi)

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

Autres conseils

Vous devriez avoir une sortie d'erreur après execvp () -. Il échouera quelque temps

exit(EXIT_FAILURE);

Comme @uncleo souligne, la liste des arguments doit avoir un pointeur NULL pour indiquer la fin:

cmdArgs[aCount] = 0;

Il est clair pour moi que vous laissez les deux programmes sont exécutés gratuitement - il semble que vous avez besoin du premier programme dans le pipeline pour terminer avant de commencer la seconde, ce qui est une recette pour le succès si les premiers blocs de programme parce que le tuyau est plein.

Jonathan a la bonne idée. Vous comptez sur le premier processus de fourche tous les autres. Chacun doit se terminer avant le prochain est fourchue.

Au lieu de cela, la fourchette des processus dans une boucle comme vous faites, mais attendez pour eux en dehors de la boucle intérieure, (au fond de la grande boucle pour l'invite du 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

Je pense que vos processus fourchues continuera à exécuter.

Essayez soit:

  • Modification à 'execvp retour'
  • Ajouter 'sortie (1);' après execvp

Un problème potentiel est que cmdargs peut avoir des ordures à la fin de celui-ci. Vous êtes censé mettre fin à ce tableau avec un pointeur NULL avant de passer à execvp ().

On dirait que grep est d'accepter STDIN, cependant, de sorte que pourrait ne pas être à l'origine des problèmes (encore).

les descripteurs de fichiers à partir du tuyau sont comptées référence, et incrémenté à chaque fourchette. pour chaque fourchette, vous devez émettre un proche des deux descripteurs afin de réduire le nombre de références à zéro et laisser le tuyau pour fermer. Je devine.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top