2009-05-22 3 views
8

this 이전 질문 나는 내 자신의 쉘 코드의 대부분을 올렸다. 내 다음 단계는 포 그라운드 및 백그라운드 프로세스 실행을 구현하고 종료 할 수 있도록 적절하게 기다려 "좀비"로 머 무르지 않는 것입니다.C의 자체 쉘에서 포어 그라운드/백그라운드 프로세스를 제대로 대기하는 방법은 무엇입니까?

백그라운드에서 실행할 가능성을 추가하기 전에 모든 프로세스가 포 그라운드에서 실행되고있었습니다. 그리고이를 위해 간단히 execvp()로 프로세스를 실행 한 후 wait (NULL)을 호출했습니다. 이제 마지막 인수로 '&'문자를 확인하고 거기에 있다면 wait (NULL)을 호출하지 않고 백그라운드에서 프로세스를 실행하고 프로세스가 백그라운드에서 행복하게 실행되어 쉘에 반환됩니다.

이것은 제대로 작동하고 있습니다. 이제 문제는 백그라운드 프로세스가 "좀비"가되지 않도록 wait() (또는 waitpid()?)를 호출해야한다는 것입니다. 그게 내 문제 야, 어떻게해야할지 모르겠다.

나는 SIGCHLD를 처리해야만한다고 믿는다. 그러나 나는 또한 SIGCHLD 신호가 보내 졌을 때 아직 완전히 이해하지 못했다. childSignalHandler()에 대기 (NULL)를 추가하지만 백그라운드에서 프로세스를 실행하자마자 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; 
} 

답변

4

당신 (POSIX 표준에서 인용) 도움이 waitpid() 다양한 옵션이 있습니다 :

WCONTINUED

을 waitpid를가() 함수는 작업 제어 정지 이후 계속해서보고 된 상태가 아닌 pid에 의해 지정된 계속되는 자식 프로세스의 상태를보고해야한다. 상태 PID로 지정된 자식 프로세스 중 하나를 즉시 이용할 수없는 경우

WNOHANG

waitpid를() 함수가 호출 스레드의 실행을 중단하지 않는다.

특히 WNOHANG에서는 시체 대기를 차단하지 않고 수집 할 시체가 있는지 확인할 수 있습니다.

호출 프로세스 설정 SA_NOCLDWAIT 갖거나 SIG_IGN 설정 SIGCHLD을 보유하고, 처리는 아니오를 기다리지 좀비 프로세스로 변환 한 어린이 호출 스레드 포함하는 프로세스의 모든 자식까지 차단한다이 없으면 호출 스레드가 종료되고 wait() 및 waitpid()가 실패하고 errno를 [ECHILD]로 설정합니다.

당신은 아마 SIGCHLD 등을 무시하고 싶지 않을 것이고, 신호 처리기는 아마도 메인 루프에 "죄송합니다, 죽은 자식이 있습니다 - 그 시체를 수집하십시오!"라고 말하는 플래그를 설정해야합니다.

SIGCONT 및 SIGSTOP 신호는 관련성이 있습니다.이 신호는 하위 프로세스를 다시 시작하고 중지하는 데 사용됩니다 (이 경우 모든 경우에 해당).

Rochkind의 책 또는 Stevens의 책을 살펴 보는 것이 좋습니다.이 책에서는 이러한 문제를 자세히 다루고 있습니다.

2

시작해야합니다. 주요 차이점은 하위 처리기를 제거하고 주 루프에 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; 
} 

편집 : 다시 신호 처리에 추가는 WNOHANG를 사용 waitpid()으로 어려운 일이 아니다. waitpid() 물건을 루프 상단에서 신호 처리기로 옮기는 것만 큼 간단합니다. 그래도 두 가지를 알아야합니다.

먼저 "전경"프로세스조차도 SIGCHLD를 보냅니다. 하나의 전경 처리 만 가능하기 때문에 전경 피이드 (부모의 반환 값 fork())를 신호 처리기에 표시되는 변수에 저장하면 전경 대 배경을 특별하게 처리 할 수 ​​있습니다.

둘째, 현재 표준 입력 (메인 루프 상단의 read())에서 I/O를 차단하고 있습니다. SIGCHLD가 발생할 때 read()에서 차단 될 가능성이 매우 높으므로 중단 된 시스템 호출이 발생합니다. OS에 따라 자동으로 시스템 호출을 다시 시작하거나 처리해야하는 신호를 보낼 수 있습니다. 대신에 전역 변수를 사용

+0

감사합니다.하지만 신호 처리기를 사용해야합니다. 실제로는 운동의 일부입니다. –

+0

방금 ​​편집을 보았습니다 ... 신호 처리기에 waitpid()를 넣었지만 두 번째 단락에서 설명하는 문제가 있습니다. 난 그냥 문제를 해결하기 위해 글로벌 변수를 사용하고 싶지 않았지만 그들 없이는 그것을 해결하는 방법을 모르겠습니다 ... –

+0

전역 변수를 피하는 것이 좋습니다,하지만 신호 처리기와 통신하는 경우 중 하나입니다 ' 필요합니다. – dwc

0

, 나는 다른 솔루션의 생각 :가 호출되지 않도록

내가 전경 프로세스를 실행하고있어 경우
if(!background) { 
    signal(SIGCHLD, NULL); 

    waitpid(pid, NULL, 0); 

    signal(SIGCHLD, childSignalHandler); 
} 

가 SIGCHLD의 핸들러를 "삭제". 그런 다음 waitpid() 다음에 핸들러를 다시 설정하십시오. 이렇게하면 백그라운드 프로세스 만 처리됩니다.

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

if(!background) 
    pause(); 

이 방법은, 프로세스 블록가 SIGCHLD 신호를 수신 할 때까지, 신호 처리기는 대기 물건을 수행합니다

+1

백그라운드 프로세스가 종료되었지만 신호 처리기가 설치되지 않은 경쟁 조건을 도입하여 치료되지 않은 자식과 결국 좀비가 생깁니다. 글로벌 변수를 피하기 위해 프로그램에 이러한 문제를 추가하지 마십시오. – dwc

+0

그러나 나는 아직도 이것에 대한 전역 변수를 구현하는 방법을 보지 않고 또한 경쟁 조건을 피한다. 이 상황을 처리하기 위해 전역 변수를 추가하면 경쟁 조건이 생깁니다. 경쟁 조건이 도입되지 않는 사례를 제공 할 때주의해야합니까? –

+0

'signal()'의 두 번째 인수는 시그널 핸들러 함수 나'SIG_IGN' 또는'SIG_DFL'에 대한 포인터 여야합니다. 'NULL '은 유효한 옵션이 아니다. SIG_IGN은 종종 널 포인터이다. 첫 번째'signal()'에서 반환 값을 캡처하고 두 번째 호출에서 반환 값을 복원해야합니다. 다른 시그널이 SIGCHLD에 도착하면'waitpid()'는 EINTR을 가지고 일찌기 리턴 할 수있다. 'signal()'대신'sigaction()'을 사용하면 어떤 신호가 허용 될지 제어 할 수 있습니다. –

2

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

+1

자식 신호가''background''를 체크하고''pause()''를 호출하는 사이에 오면 어떨까요? 경쟁 조건이 있습니다. – jcoffland