좀비 프로세스 생성과 소멸

좀비 프로세스는 껍데기만 남아있는 상태이다


좀비 프로세스란?

  • zombie process, defunct process라고도 한다
  • 실행이 종료되었지만 os의 process table을 점유하고 있는 프로세스
    • os가 관리하는 process table의 크기에는 제한이 있기 때문에 좀비 프로세스가 계속 생성되면 더 이상 프로세스 생성이 불가능할 수 있다
  • parent가 자신의 exit status를 확인하기를 기다리고 있는 상태이다
    • child의 exit status를 확인하는 방법에는 두 가지가 있다

example code

  • child process는 defunct process가 되는 예제
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, const char *argv[])
{
  pid_t p;

  if ((p = fork()) == 0) // child
    return 0;
  else                   // parent
    while (1) { sleep(10); }

  return 0;
}
  • ps -ef | grep <프로세스 이름> 실행 --> [프로세스 이름] \<defunct> 출력

defunct process

좀비 프로세스 발생을 막는 방법

  • wait류 함수 호출
  • signal handler 등록

wait

  • wait를 호출하면 child process의 main 함수가 반환한 값을 확인할 수 있다
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>

int main(int argc, const char *argv[])
{
  pid_t p;
  int estatus;

  if ((p = fork()) == 0) // child
    return 99;
  else {                 // parent
    wait(&estatus);
    printf("child(%d) exit status: %d\n",
        p, estatus);
    while (1) sleep(10);
  }

  return 0;
}

signal

  • child의 main 함수가 반환하면 parent에게 SIGHLD signal이 전달되는데 이 시그널을 받았을때 waitpid로 종료된 child의 exit status를 읽을 수 있다
  • sigaction을 사용하면 signal에 대해 signal함수보다 섬세한 제어가 가능하다. signal보다는 sigaction 사용이 권장된다
    • signal함수는 핸들러가 실행되는 동안 새로운 시그널이 프로세스에 전달되는 것을 막지 않는다. 하지만 sigaction은 시그널 핸들러가 반환하기 전까지 다른 시그널을 막는다(block)
    • signal 함수는 보통 시그널 핸들러가 한 번 호출되고 나면 해당 시그널에 대한 핸들러를 SIG_DFL로 초기화한다
      • 근데 내 환경(6.8.0-48-generic #48~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC x86_64)에서는 시그널 핸들러가 유지됐다
    • signal함수의 위와 같은 동작은 UNIX 구현체마다 다르다. 표준이 이를 허용하고 있다
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>

#define DO_USEFUL_TASKS do { sleep(10); } while(1);

void sigchld_handler(int signo)
{
  pid_t p;
  int estatus;
  int opt = 0;
  waitpid(-1, &estatus, opt);
  printf("child(%d) exit status: %d\n",
      p, estatus);
}

int main(int argc, const char *argv[])
{
  pid_t p;
  int estatus;

  if ((p = fork()) == 0) {// child1
    printf("child(%d) exit\n", getpid());
    return 1;
  }
  else {                 // parent
    signal(SIGCHLD, sigchld_handler);
    // signal(SIGCHLD, SIG_DFL);                // 좀비 생성
    // signal(SIGCHLD, SIG_IGN);                // 좀비 생성 안됨
  }

  if ((p = fork()) == 0) {// child2
    printf("child(%d) exit\n", getpid());
    return 2;
  }

  DO_USEFUL_TASKS

  return 0;
}

references