본문 바로가기

Network/중급 TCP/IP socket

03. fork() / 좀비프로세스 / wait waitpid / sigchld

fork()함수


프로그램내 동작하고 있는 프로세스를 복사하고 복사한 프로세스를 독립적으로 돌려주는 함수입니다 흔히 원복 프로세서를 부모프로세스라 부르고 복사한 프로세스를 자식프로세스라 부릅니다
.

 

헤더    unistd.h

형태    pid_t fork(void); 

반환    pid_t 실행에 실패하면 -1 을 반환. 부모에게는 새로 생성된 자식 프로세스 PID가 반환되며, 자식 프로세스에는 0이 반환됩니다.

 

#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int global_var = 0;

int main()
{
  pid_t ret_pid, child;
  int local_var = 0;

  ret_pid = fork();
  if(ret_pid < 0)
  {
    printf("fork() error\n");
    return -100;
  }
  else if(ret_pid == 0)
  {
    global_var++;
    local_var++;
    printf("CHILD - my PID : %d parents PID : %d \n", getpid(),getppid());
  }
  else
  {
    sleep(10);

    global_var += 5;
    local_var += 5;
    printf("PARENT - my pid : %d chail's PID : %d\n", getpid(),ret_pid);
  }
  printf("\t global_var : %d \n",global_var);
  printf("\t local_var : %d \n",local_var);
  return 0;
}


 

결과>>


 

좀비 프로세스

-  프로세스 종료 후 메모리상에서 사라지지 않은 프로세스

 

좀비 프로세스 생성 이유

-  자식  프로세스가  종료하면서  반환된    0  커널이  부모  프로세스에  전달한    자식

프로세스를 소멸시킴.  

-  반환값을 부모 프로세스에 전달하지 못한 경우 자식 프로세스는 좀비로 존재.

-  부모 프로세스가 커널에게 종료된 자식 프로세스의 리턴값을 전달 요청을 해야만  

커널이 리턴값 전달 가능함

 

종료된 자식 프로세스의 리턴값 요청 함수
 
1. wait 함수
 
#include <sys/types.h>
#include <sys/wait.h>
 
pid_t wait(int * status);
성공시 종료된 자식 프로세스  ID, 실패시  -1 리턴
 
-  status : 포인터  status가 가리키는 변수에 자식 프로세스 종료시 리턴하거나  
exit 함수 호출시 전달한 인자값이 저장됨
-  매크로함수 WEXITSTATUS(status)로 종료시 리턴값이나  exit() 인자로 넘겨진 값 확인
-  호출 시점에 종료된 자식 프로세스가 없으면  blocking 상태에 빠짐
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int global_var = 0;

int main()
{
  pid_t ret_pid, child;
  int local_var = 0;
  int state;

  ret_pid = fork();
  if(ret_pid < 0)
  {
    printf("fork() error\n");
    return -100;
  }
  else if(ret_pid == 0)
  {
    global_var++;
    local_var++;
    printf("CHILD - my PID : %d parents PID : %d \n", getpid(),getppid());
  }
  else
  {
    global_var += 5;
    local_var += 5;
    printf("PARENT - my pid : %d chail's PID : %d\n", getpid(),ret_pid);
    
    child = wait(&state);
    printf("\t Child PID = %d \n", child);
    printf("\t return value = %d \n", WEXITSTATUS(state));
  
    sleep(10);
  }

  printf("\t global_var : %d \n",global_var);
  printf("\t local_var : %d \n",local_var);
  
  return 0;
}


 

결과>>


 

 2. waitpid 함수  (wait함수의  blocking 상태 해결)

 

#include <sys/types.h>

#include <sys/wait.h>

 

pid_t waitpid(pid_t pid, int * status, int options);

성공시 종료된 자식 프로세스ID(경우에 따라 0), 실패시  -1 리턴

 

-  pid :   종료 확인을 원하는 자식 프로세스  ID. 임의의 자식 프로세스인 경우  -1 대입.

-  status :  wait 함수의  status와 같은 역할

-  options :  sys/wait.h에 정의된  WNOHANG’ 상수를 인자로 전달하게 되면 이미 종료한

          자식 프로세스가 없는 경우 blocking 상태로 가지않고 바로 리턴함.

       이때 waitpid 함수의 리턴값은  0이 된다.

  

#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int global_var = 0;

int main()
{
  pid_t fork_ret, child;
  int local_var = 0;
  int state;

  fork_ret = fork();
  if(fork_ret < 0)
  {
    printf("fork() error\n");
    exit(1);
  }
  else if(fork_ret == 0)
  {
    global_var++;
    local_var++;
    printf(" CHILD - my PID : %d parent's PID : %d \n", getpid(),getppid());
    sleep(10);
  }
  else
  {
    global_var += 5;
    local_var += 5;
    printf("PARENT - my PID : %d child's PID : %d \n", getpid(),fork_ret);

    do
    {
      sleep(3);
      puts("3초 대기");

      child = waitpid(-1&state, WNOHANG);
    }
    while(child == 0);

    printf("\t 종료된 자식 프로세스 ID = %d \n", child);
    printf("\t 종료된 자식 프로세스의 리터 값 = %d \n", WEXITSTATUS(state));
  }
  printf("\t global_var : %d \n", global_var);
  printf("\t local_var : %d \n", local_var);
  
  return 0;

}


 

 결과>>

 

waitpid 함수의 호출 시점은??

-  자식 프로세스가 종료되는 순간 부모 프로세스가 waitpid 함수를 호출하도록 해야함

-  자식 프로세스가 종료된 시점에 발생하는 시그널  SIGCHLD를 이용

-  sigaction 함수를 사용하여  SIGCHLD가 발생시 부모 프로세스가 자식 프로세스의 리턴

값을 읽도록 함

 


좀비 프로세스확인 

 

 결과>> wait함수 실행시 ps -u를 입력하여 확인합니다


STAT의 Z가 zombie를 뜻합니다


STAT에 Z가 없는 것을 확인 할 수 있습니다



시그널(SIGCHLD)의 이용

SIGCHLD : (시그널무시)자식프로세서 종료

sigemptyset() 주어진 마스크 셋의 모든 필드를 설정하거나 헤제한다

#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

void z_handler();

int global_var = 0;

int main()
{
  pid_t fork_ret, child;
  int local_var = 0;
  int state;

  struct sigaction act;
  act.sa_handler = z_handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags =0;

  state = sigaction(SIGCHLD, &act,0);
  if(state != 0)
  {
    puts("sigaction() error\n");
    exit(1);
  }
  
  fork_ret = fork();

  if(fork_ret < 0)
  {
    printf("fork() error\n");
    exit(1);
  }
  else if(fork_ret == 0)
  {
    global_var++;
    local_var++;
    printf(" CHILD - my PID : %d parent's PID : %d \n", getpid(),getppid());
  }
  else
  {
    global_var += 5;
    local_var += 5;
    printf("PARENT - my PID : %d child's PID : %d \n", getpid(),fork_ret);
    
    sleep(5);
  }
  

  printf("\t global_var : %d \n", global_var);
  printf("\t local_var : %d \n", local_var);
  
  return 0;

}

void z_handler()
{
  pid_t child;
  int state;
  
  child = waitpid(-1&state, WNOHANG);

  printf("\t 소멸된 자식 프로세스 ID = %d \n", child);
  printf("\t 소멸된 자식 프로세스의 리턴 값 = %d \n", WEXITSTATUS(state));
}

 

결과>>

 
**정상적으로 자식프로세스가 종료된것을 확인 할 수있습니다