C의 내 쉘에서 전경/배경 프로세스를 올바르게 기다리는 방법은 무엇입니까?

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

문제

~ 안에 이것 이전 질문 나는 대부분의 쉘 코드를 게시했습니다. 다음 단계는 전경 및 배경 프로세스 실행을 구현하고 "좀비"로 유지되지 않도록 종료 될 때까지 기다리는 것입니다.

백그라운드에서 실행할 가능성을 추가하기 전에 모든 프로세스가 전경에서 실행되었습니다. 이를 위해 execvp ()로 모든 프로세스를 실행 한 후 간단히 Wait (Null)라고 불렀습니다. 이제, 나는 마지막 인수로 '&'캐릭터를 확인하고 그것이 있다면, 대기 (null)를 호출하지 않음으로써 백그라운드에서 프로세스를 실행하면 백그라운드에서 프로세스가 행복하게 실행될 수 있습니다.

이것은 모두 제대로 작동합니다 (생각합니다). 문제는 이제 백그라운드 프로세스가 "좀비"로 남아 있지 않도록 Wait () (또는 Waitpid ()?)에게 전화해야한다는 것입니다. 그게 내 문제 야, 어떻게 해야할지 잘 모르겠어요 ...

나는 sigchld를 처리하고 거기에서 무언가를해야한다고 생각하지만, Sigchld 신호가 childsignalhandler ()에 대기 (null)를 추가하려고했기 때문에 Sigchld 신호가 전송 될 때 완전히 이해하지 못했지만 나는 즉시 작동하지 않았기 때문에 작동하지 않았습니다. 백그라운드에서 프로세스를 실행 한 ChildSignalHandler () 함수가 호출되었으며 결과적으로 대기 (NULL)이므로 "배경"프로세스가 끝날 때까지 쉘로 아무것도 할 수 없습니다. 신호 핸들러의 대기로 인해 더 이상 배경에서 실행되지 않았습니다.

이 모든 것에서 내가 무엇을 놓치고 있습니까?

마지막으로,이 연습의 일부는 프로세스 종료와 같은 프로세스 상태의 변경 사항을 인쇄해야합니다. 따라서 그것에 대한 통찰력도 정말 감사합니다.

이것은 현재 내 전체 코드입니다.

#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;
}
도움이 되었습니까?

해결책

다양한 옵션이 있습니다 waitpid() 당신을 돕기 위해 (POSIX 표준의 인용문) :

wcontinued

Waitpid () 함수는 PID가 지정된 지속적인 아동 프로세스의 상태를보고해야합니다.

Wnohang

Waitpid () 함수는 PID가 지정한 자식 프로세스 중 하나에 대해 즉시 사용할 수없는 경우 호출 스레드의 실행을 중단하지 않아야합니다.

특히 Wnohang은 프로세스가 시체 대기를 차단하지 않고 수집 할 시체가 있는지 확인할 수 있습니다.

호출 프로세스에 sa_nocldwait 설정이 있거나 sigchld가 sig_ign으로 설정되어 있고 그 과정에는 좀비 프로세스로 변형 된 어린이가없는 아동이 없으면, 호출 스레드는 호출 스레드 종료 및 프로세스의 모든 어린이들에게는 차단해야합니다. 대기 () 및 waitpid ()는 실패하고 errno를 [echild]로 설정해야합니다.

당신은 아마도 sigchld 등을 무시하고 싶지 않을 것입니다. 그리고 당신의 신호 핸들러는 아마도 당신의 메인 루프 "죄송합니다. 죽은 아이가 있습니다 - 그 시체를 모으십시오!"

SIGCONT 및 SIGSTOP 신호는 또한 귀하와 관련이 있습니다. 각각 아동 프로세스를 다시 시작하고 중지하는 데 사용됩니다 (이 맥락에서 어떤면에서)

Rochkind의 책이나 Stevens의 책을 보는 것이 좋습니다. 이러한 문제를 자세히 다룹니다.

다른 팁

이것은 당신을 시작해야합니다. 주요 차이점은 내가 어린이 처리기를 제거하고 추가했다는 것입니다. waitpid 피드백이 포함 된 메인 루프에서. 테스트 및 작업이지만 분명히 더 많은 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;
}

편집하다: 신호 처리에 다시 추가하는 것은 어렵지 않습니다 waitpid() Wnohang 사용. 움직이는 것만 큼 간단합니다 waitpid() 루프 상단에서 신호 핸들러로의 물건. 그래도 두 가지를 알고 있어야합니다.

첫째, "전경"프로세스조차도 Sigchld를 보냅니다. 전경 프로세스가 하나만있을 수 있으므로 전경 PID (부모의 반환 값에서 fork()) 전경 대 백그라운드를 특수한 처리하려면 신호 핸들러에 가시가있는 가변에서.

둘째, 현재 표준 입력에서 I/O 차단을하고 있습니다 ( read() 메인 루프 상단에서). 당신은 막을 수있을 가능성이 매우 높습니다 read() Sigchld가 발생하면 시스템 호출이 중단됩니다. OS에 따라 시스템 호출을 자동으로 다시 시작하거나 처리 해야하는 신호를 보낼 수 있습니다.

당신은 사용할 수 있습니다 :

if(!background)
    pause();

이렇게하면 프로세스가 수신 될 때까지 차단됩니다. SIGCHLD 신호 및 신호 핸들러는 대기 작업을 수행합니다.

글로벌 변수를 사용하는 대신 다른 솔루션을 생각했습니다.

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

    waitpid(pid, NULL, 0);

    signal(SIGCHLD, childSignalHandler);
}

전경 프로세스를 실행하는 경우 Sigchld 용 처리기를 "삭제"하여 호출되지 않습니다. 그런 다음 WaitPid () 후에 핸들러를 다시 설정하십시오. 이런 식으로 배경 프로세스 만 처리됩니다.

이 솔루션에 문제가 있다고 생각하십니까?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top