min's devlog
SIGCHLD, wait() 본문
프로세스의 종료
모든 프로세스는 종료할 때 자신의 부모프로세스에게 SIGCHILD 시그널을 보낸다.
부모 프로세스는 이 시그널을 받아 처리할 수 있다.
if fork 결과 값이 0이면 childprocess. child 종료시 fork로 sigchild라는 시그널을 보내고 종료한다.
child의 종료상태를 알려면 wait라는 함수를 이용해서 child가 종료할때까지 기다릴 수 있다.
wait()
부모프로세스가 자식의 종료시점을 알거나 종료 상태 값을 알기위해 wait()나 waitpid() 함수를 이용한다.
pid_t pid;
int stat;
//부모 프로세스의 일처리
pid = wait(&stat);//자식 프로세스가 종료될 때까지 이곳에서 블록됨
//자식 프로세스 종료 후에 처리할 일
- wait로 자식 프로세스의 종료상태를 알 수 있다. 종료상태 값은 stat 인자로 리턴
- 자식 프로세스가 종료된 후 부모프로세스가 종료상태를 읽어가지 않으면 자식 프로세스는 좀비상태가 된다.
- 좀비 프로세스가 남지 않도록 해야한다. -> SIGCHLD 핸들러처리/wait()함수로 자식 프로세스의 종료 파악
좀비상태는 나중에 자식 프로세스의 종료상태를 읽기 위해서 만들어졌다.
프로세스의 종료처리
- SIGCHLD 시그널에 대해 SIG_DFL이나 핸들러 함수를 설정하면 wait() 호출 이전에 종료한 자식 프로세스는 좀비가 된다.
- SIG_IGN을 등록했을 때는 좀비 프로세스가 만들어지지 않는다.
- sact.sa_flags = SA_NOCLDWAIT;SA_NOCLDWAIT 설정시에 좀비 프로세스 생성을 막을 수 있다.
wait() : sig_IGN등록
//wait test1.c
//gcc -o wait_test1 wait_test1.c
//실행 : wait_test
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<errno.h>
//사용자 시그널 핸들러 함수
void catch_sigchld(int signo)
{
puts("###(Parent) catch SIGCHLD"); //하는건 그냥 catch만 했단걸 알려줌
}
int chstat; //종료상태값
int main(int argc, char *argv[])
{
int i,n;
struct sigaction sact;
sact.sa_flags=0;
sigemptyset(&sact.sa_mask); //mask는 비워놓음
sigaddset(&sact.sa_mask, SIGCHLD); //빈 상태에서 SIGCHILD만 등록함
//시그널 핸들러 등록
sact.sa_handler=SIG_DFL; //기본동작을 default(무시)로 설정 (SIGCHILD발생하면 무시한다)
//sact.sa_handler=SIG_IGN; //시그널 무시 등록
//sact.sa_handler=catch_sigchld; //사용자 핸들러 등록
sigaction(SIGCHLD,&sact,NULL); //&sact에서 act는 SIG_DFT모드. 시그널모드가 default모드인 경우에 어떻게 될지? 를 살펴본다.
//자식 프로세스 생성
for(i=0; i<5; i++)
{
if(fork()==0){ //fork가 0이면 child process
if(i>2) //2보다 큰 3,4번 자식프로세스는 6초쉬기
sleep(6); //부모의 호출보다 늦게 종료
printf("%d번 Child),PID(%d),PPID(%d) Exited\n",i,getpid(),getppid()); //나머진 출력후
exit(13); //종료값 13번
}
} //for
//parent process (3,4번은 6초기다리고 printf 출력하고 있음)
sleep(3); //0,1,2번 자식이 종료하길 기다리며 parent는 sleep. 그동안 아래출력
puts("-------------");
system("ps-a"); //system : 명령어를 바로 실행할수 있게함
puts("-------------");
//3초 경과후 parent가 깨어나 wait호출 (3,4는 3초가 남음)
puts("#(Parent) wait호출함");
for(;;)
{
chstat = -1; //초기화 child status
n=wait(&chstat); //wait로 3,4번의 종료를 기다림
printf("#wait=%d(child stat=%d)\n",n,chstat); //wait전 이미 종료된0,1,2 출력
//좀비프로세스가 되기는 하지만, 어떤상태인지는 이렇게 chstat으로 나중에라도 알 수 있다.
if(n==-1){ //더이상 기다릴 프로세스 없다
if(errno==ECHILD) //마찬가지로 더이상 기다릴 프로세스 없다
{
perror("기다릴자식 프로세스가 존재하지 않음");
break; //반복문 빠져나와 parent process 종료
}
else if(errno==EINTR)
{
perror("wait시스템콜이 인터럽트됨");
continue;
}
}
}
puts("#(Parent)종료함");
return 0;
}
//sact.sa_handler=SIG_DFL; //기본동작을 default(무시)로 설정 (SIGCHILD발생하면 무시)
So 이 코드에서는 void catch_sigchld(into signo)를 수행하지 않는다.
//자식0,1,2는 그냥 출력-자식3,4는 6초 기다린 후에 prinf출력
Child가 0번~2번,4,3번까지 있다. ->fork() 를 5회
0번~2번은 PID(자식 ID), PPID(부모ID) 출력 후 바로 종료되었다.
그리고 부모가 wait를 호출하기 전에 종료되었다. --> 0,1,2번은 좀비상태
ps -a(현재시스템의 프로세스 상태 확인) 함수를 호출한다. <defunct>상태의 프로세스를 볼 수 있다. defuntct=좀비상태
특히 PID를 대조해보며 어떤 프로세스가 좀비프로세스인지 확인할 수 있다.
부모 프로세스가 wait() 하기 전에 종료를 해서 좀비상태가 된 것이다!
부모가 wait을 호출해서 자식 프로세스의 종료를 기다린다.
위 좀비 프로세스는 이미 종료되어있다. (#wait=~~~ 의 stat를 보면 죽었음을 알고있다.)
그리고 3번, 4번도 종료.
코드의 일부분을 달리 쓸 때
//시그널 핸들러 등록
sact.sa_handler=SIG_DFL; //기본동작을 default(무시)로 설정 (SIGCHILD발생하면 무시한다)
//sact.sa_handler=SIG_IGN; //시그널 무시 등록
//sact.sa_handler=catch_sigchld; //사용자 핸들러 등록
부분에서,
1. 원래 코드-윗부분 빼고 주석처리 : 기본동작이 무시, 좀비프로세스가 발생한다.
2. sact.sa_handler=SIG_IGN; 빼고 주석처리하면 : 무시를 하되 컴파일시 좀비프로세스가 발생하지 않는다
3. sact.sa_handler=catch_sigchld; 빼고 주석처리 : 시그널 핸들러
'Linux > TCP IP Socket Programming' 카테고리의 다른 글
UDP 에코 서버 (0) | 2021.04.10 |
---|---|
파이프 기반의 프로세스간 통신 (0) | 2021.04.10 |
alarm() 인터럽트 (0) | 2021.04.09 |
System Call 수행 중 함수 처리 (0) | 2021.04.09 |
System Call 수행 중의 시그널처리 (0) | 2021.04.09 |