C의 내 쉘에서 전경/배경 프로세스를 올바르게 기다리는 방법은 무엇입니까?
-
23-08-2019 - |
문제
~ 안에 이것 이전 질문 나는 대부분의 쉘 코드를 게시했습니다. 다음 단계는 전경 및 배경 프로세스 실행을 구현하고 "좀비"로 유지되지 않도록 종료 될 때까지 기다리는 것입니다.
백그라운드에서 실행할 가능성을 추가하기 전에 모든 프로세스가 전경에서 실행되었습니다. 이를 위해 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 () 후에 핸들러를 다시 설정하십시오. 이런 식으로 배경 프로세스 만 처리됩니다.
이 솔루션에 문제가 있다고 생각하십니까?