Comment attendre bien pour les processus de premier plan / arrière-plan dans ma coquille en C?

StackOverflow https://stackoverflow.com/questions/900411

Question

cette question précédente que j'ai posté plus de mon propre code shell. Ma prochaine étape consiste à mettre en œuvre l'exécution de premier plan et en arrière-plan et bien les attendre de mettre fin afin qu'ils ne restent pas « zombies ».

Avant d'ajouter la possibilité de les exécuter en arrière-plan, tous les processus en cours d'exécution ont été au premier plan. Et pour cela, j'ai simplement appelé attente (NULL) après l'exécution d'un processus avec execvp (). Maintenant, je vérifie le caractère « & » comme dernier argument et si elle est là, exécutez le processus en arrière-plan en ne demandant pas d'attente (NULL) et le processus peut fonctionner avec bonheur dans l'arrière-plan, je suis retourné à ma coquille.

Ceci est tout fonctionne correctement (je pense), le problème maintenant est que je dois aussi appeler wait () (ou waitpid ()?) D'une certaine manière pour que le processus d'arrière-plan ne reste pas « zombie ». C'est mon problème, je ne sais pas comment faire ...

Je crois que je dois gérer SIGCHLD et faire quelque chose là-bas, mais je dois encore comprendre lorsque le signal SIGCHLD est envoyé parce que j'ai essayé d'ajouter également attente (NULL) à childSignalHandler (), mais cela n'a pas fonctionné parce que dès que j'exécuté un processus en arrière-plan, la fonction childSignalHandler () a été appelée et, par conséquent, l'attente (NULL), ce qui signifie que je ne pouvais rien faire avec ma coquille jusqu'à ce que le processus « de fond » terminé. Ce qui n'a pas été en cours d'exécution sur le fond plus à cause de l'attente dans le gestionnaire de signal.

Qu'est-ce que je manque dans tout cela? I

Une dernière chose, une partie de cet exercice, je dois aussi imprimer les changements de l'état des processus, comme la fin du processus. Ainsi, toute idée sur c'est aussi très apprécié.

Ceci est mon code complet au moment:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>

#include "data.h" // Boolean typedef and true/false macros


void childSignalHandler(int signum) {
    //
}

int main(int argc, char **argv) {
    char bBuffer[BUFSIZ], *pArgs[10], *aPtr = NULL, *sPtr;
    bool background;
    ssize_t rBytes;
    int aCount;
    pid_t pid;

    //signal(SIGINT, SIG_IGN);

    signal(SIGCHLD, childSignalHandler);

    while(1) {
        write(1, "\e[1;31mmyBash \e[1;32m# \e[0m", 27);
        rBytes = read(0, bBuffer, BUFSIZ-1);

        if(rBytes == -1) {
            perror("read");
            exit(1);
        }

        bBuffer[rBytes-1] = '\0';

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

        sPtr = bBuffer;
        aCount = 0;

        do {
            aPtr = strsep(&sPtr, " ");
            pArgs[aCount++] = aPtr;
        } while(aPtr);

        background = FALSE;

        if(!strcmp(pArgs[aCount-2], "&")) {
            pArgs[aCount-2] = NULL;
            background = TRUE;
        }

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

            if(pid == -1) {
                perror("fork");
                exit(1);
            }

            if(pid == 0) {
                execvp(pArgs[0], pArgs);
                exit(0);
            }

            if(!background) {
                wait(NULL);
            }
        }
    }

    return 0;
}
Était-ce utile?

La solution

Il existe différentes options pour waitpid() pour vous aider (des citations de la norme POSIX):

  

WCONTINUED

     

La fonction waitpid () rend compte de l'état de tout processus d'enfant continue spécifié par pid dont le statut n'a pas été signalé depuis qu'il a continué d'un arrêt de contrôle du travail.

     

WNOHANG

     

La fonction waitpid () ne doit pas suspendre l'exécution du thread appelant si le statut est pas immédiatement disponible pour l'un des processus enfants spécifiés par pid.

En particulier, WNOHANG vous permettra de voir s'il y a des cadavres à recueillir sans causer votre processus de bloquer en attente d'un cadavre.

  

Si le processus d'appel a SA_NOCLDWAIT établi ou a SIGCHLD à SIG_IGN, et le processus n'a pas unwaited-pour les enfants qui ont été transformées dans les processus de zombies, le thread appelant doit bloquer jusqu'à ce que tous les enfants du processus contenant le fil d'appel fin, et attendre () et waitpid () doit échouer et errno [ECHILD].

Vous ne voulez probablement pas ignorer SIGCHLD, etc, et votre gestionnaire de signal devrait probablement installerez un drapeau pour dire à votre boucle principale « Oops, il est enfant mort - aller recueillir ce cadavre! »

.

Les signaux SIGCONT et SIGSTOP seront également pertinentes pour vous -. Ils sont utilisés pour redémarrer et arrêter un processus enfant, respectivement (dans ce contexte, en tout cas)

Je vous recommande regarder le livre de Rochkind ou le livre de Stevens - ils couvrent ces questions en détail

.

Autres conseils

Cela devrait vous aider à démarrer. La principale différence est que je me suis débarrassé du gestionnaire d'enfant et ajouté waitpid dans la boucle principale avec des commentaires. Testé et de travail, mais il faut évidemment plus TLC.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>

int main(int argc, char **argv) {
        char bBuffer[BUFSIZ], *pArgs[10], *aPtr = NULL, *sPtr;
        int background;
        ssize_t rBytes;
        int aCount;
        pid_t pid;
        int status;
        while(1) {
                pid = waitpid(-1, &status, WNOHANG);
                if (pid > 0)
                        printf("waitpid reaped child pid %d\n", pid);
                write(1, "\e[1;31mmyBash \e[1;32m# \e[0m", 27);
                rBytes = read(0, bBuffer, BUFSIZ-1);
                if(rBytes == -1) {
                        perror("read");
                        exit(1);
                }
                bBuffer[rBytes-1] = '\0';
                if(!strcasecmp(bBuffer, "exit")) 
                        exit(0);
                sPtr = bBuffer;
                aCount = 0;
                do {
                        aPtr = strsep(&sPtr, " ");
                        pArgs[aCount++] = aPtr;
                } while(aPtr);
                background = (strcmp(pArgs[aCount-2], "&") == 0);
                if (background)
                        pArgs[aCount-2] = NULL;
                if (strlen(pArgs[0]) > 1) {
                        pid = fork();
                        if (pid == -1) {
                                perror("fork");
                                exit(1);
                        } else if (pid == 0) {
                                execvp(pArgs[0], pArgs);
                                exit(1);
                        } else if (!background) {
                                pid = waitpid(pid, &status, 0);
                                if (pid > 0)
                                        printf("waitpid reaped child pid %d\n", pid);
                        }
                }
        }
        return 0;
}

EDIT: Ajout de retour dans le traitement du signal est difficile avec l'aide waitpid() WNOHANG. Il est aussi simple que de déplacer l'étoffe waitpid() du haut de la boucle dans le gestionnaire de signal. Vous devez être conscient de deux choses, cependant:

Tout d'abord, même les processus « de premier plan », il enverra SIGCHLD. Comme il peut y avoir qu'un seul processus de premier plan, vous pouvez simplement stocker l'avant-plan pid (la valeur de retour des parents de fork()) dans une variable visible au gestionnaire de signal si vous voulez faire un traitement spécial de premier plan par rapport à fond.

Deuxièmement, vous faites actuellement le blocage d'E / S sur l'entrée standard (le read() en haut de la boucle principale). Vous risquez fort d'être bloqué sur read() lorsque SIGCHLD se produit, entraînant un appel système interrompu. Selon OS, il peut redémarrer l'appel système automatiquement, ou il peut envoyer un signal que vous devez gérer.

Vous pouvez utiliser:

if(!background)
    pause();

De cette façon, les blocs de processus jusqu'à ce qu'il reçoive le signal de SIGCHLD, et le gestionnaire de signal fera les choses d'attente.

Au lieu d'utiliser une variable globale, je pensais à une autre solution:

if(!background) {
    signal(SIGCHLD, NULL);

    waitpid(pid, NULL, 0);

    signal(SIGCHLD, childSignalHandler);
}

Si je suis en cours d'exécution d'un processus d'avant-plan « supprimer » le gestionnaire de SIGCHLD afin qu'il ne soit pas appelé. Puis, après waitpid (), réglez à nouveau le gestionnaire. De cette façon, seuls les processus d'arrière-plan seront traitées.

Pensez-vous qu'il ya quelque chose de mal avec cette solution?

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