min's devlog

SIGCHLD, wait() 본문

Linux/TCP IP Socket Programming

SIGCHLD, wait()

값진 2021. 4. 9. 16:29

프로세스의 종료

모든 프로세스는 종료할 때 자신의 부모프로세스에게 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; 빼고 주석처리 : 시그널 핸들러

 

2번 결과

 

3번 결과

 

'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
Comments