Como esperar corretamente para processos de primeiro plano / fundo na minha própria concha em C?

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

Pergunta

Na esta pergunta anterior eu postei mais do meu próprio código shell. Meu próximo passo é implementar a execução do primeiro plano e processo de fundo e devidamente esperar por eles para terminar de modo que não fique como "zumbis".

Antes de adicionar a possibilidade de executá-los no fundo, todos os processos foram executado em primeiro plano. E, para isso, eu simplesmente chamada wait (NULL) depois de executar qualquer processo com execvp (). Agora, eu verificar o caractere '&' como o último argumento e se ele está lá, executar o processo em segundo plano não chamando wait (NULL) e o processo pode ser executado feliz no fundo irá Eu estou voltou a minha concha.

Isto é tudo a funcionar correctamente (eu acho), o problema agora é que eu também preciso de espera de chamada () (ou waitpid ()?) De alguma forma para que o processo de fundo não permanece "zombie". Esse é o meu problema, eu não tenho certeza de como fazer isso ...

Eu acredito que eu tenho que lidar com SIGCHLD e fazer algo lá, mas eu ainda tenho que entender completamente quando o sinal SIGCHLD é enviado porque eu tentei adicionar também wait (NULL) para childSignalHandler (), mas não funcionou porque, como assim que eu executado um processo em segundo plano, a função childSignalHandler () foi chamado e, consequentemente, o tempo de espera (NULL), ou seja, eu não podia fazer nada com minha concha até que o processo "background" terminou. Que não estava funcionando no fundo mais por causa da espera no manipulador de sinal.

O que estou ausente em tudo isso?

Uma última coisa, parte deste exercício Eu também preciso imprimir as alterações do estatuto processos, como o encerramento do processo. Então, qualquer visão sobre o que é também muito apreciado.

Este é o meu código completo no momento:

#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;
}
Foi útil?

Solução

Existem várias opções para waitpid() para ajudá-lo (citações do padrão POSIX):

WCONTINUED

A função waitpid () deve comunicar o status de qualquer processo filho contínuo especificado por pid cujo status não foi relatado, uma vez que continuou de uma paragem de controle de trabalho.

WNOHANG

A função waitpid () não deve suspender a execução do segmento de chamada se o estado não está imediatamente disponível para um dos processos filho especificados por pid.

Em particular, WNOHANG irá permitir que você veja se existem quaisquer cadáveres para recolher sem causar o seu processo para bloquear à espera de um cadáver.

Se o processo de chamada tem um conjunto SA_NOCLDWAIT ou tem um conjunto SIGCHLD para SIG_IGN, eo processo não tem unwaited-para as crianças que foram transformadas em processos zombie, o segmento de chamada deve bloquear até que todos os filhos do processo que contém o segmento de chamada terminar, e wait () e waitpid () deve falhar e setar errno para [ECHILD].

Você provavelmente não quer estar ignorando SIGCHLD, etc, e seu manipulador de sinal provavelmente deve ser a criação de uma bandeira de dizer ao seu loop principal. "Oops!; Há criança morta - vão cobrar que o cadáver"

Os sinais SIGCONT e SIGSTOP também será de importância para você -. Eles são usados ??para reiniciar e parar um processo filho, respectivamente (neste contexto, de qualquer modo)

Eu recomendo a olhar para o livro de Rochkind ou livro Stevens' - eles cobrem estas questões em detalhe

.

Outras dicas

Este deve começar. A principal diferença é que eu me livrei do manipulador criança e acrescentou waitpid no circuito principal com algum feedback. Testado e funcionando, mas, obviamente, precisa de mais 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: Adicionando volta na manipulação de sinal não é difícil com waitpid() usando WNOHANG. É tão simples como mover o material waitpid() a partir do topo do loop para o manipulador de sinal. Você deve estar ciente de duas coisas, no entanto:

Em primeiro lugar, até mesmo processos de "primeiro plano" enviará SIGCHLD. Uma vez que não pode ser apenas processo de um plano você pode simplesmente armazenar o pid primeiro plano (valor de retorno do pai de fork()) de modo visível variável para o manipulador de sinal, se você quer fazer um tratamento especial do fundo plano vs..

Em segundo lugar, você está fazendo atualmente o bloqueio I / O na entrada padrão (o read() no topo loop principal). Você é extremamente provável a ser bloqueado em read() quando SIGCHLD ocorre, resultando em uma chamada de sistema interrompida. Dependendo OS pode reiniciar a chamada de sistema automaticamente, ou pode enviar um sinal de que você deve lidar.

Você pode usar:

if(!background)
    pause();

Desta forma, os blocos de processo até que ele recebe o sinal SIGCHLD, eo manipulador de sinal vai fazer as coisas espera.

Em vez de usar uma variável global, pensei em uma solução diferente:

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

    waitpid(pid, NULL, 0);

    signal(SIGCHLD, childSignalHandler);
}

Se eu estou correndo um processo em primeiro plano "apagar" o manipulador para SIGCHLD para que ele não obter chamado. Então, depois de waitpid (), definir o manipulador de novo. Desta forma, apenas os processos em segundo plano serão tratadas.

Você acha que há algo de errado com essa solução?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top