본문 바로가기
개발/Server

[C언어] 좀비 프로세스를 없애는 더블 fork()..

by 열야 2012. 8. 28.

fork()를 이용하여 서버 프로그래밍을 할 시, 유용한 방법을 소개하기로 한다. 이는 Steven아저씨 책 Advanced Programming in the Unix Environment 202쪽에 나와 있는 방법이기도 하다.


우선 좀비 프로세스에 대해서 알아야 한다.

좀비 프로세스:

프로세스가 뒤졌는데, 아무도 이놈의 장례를 치뤄주지 않아 뒤진 상태로 리소스를 몽땅 가지고 있는 상태..


뭐 이미 알겠지만, 프로세스가 뒤지면 가지고 있던 파일들 할당 받았던 모든 메모리를 자동으로 모두 해제되고 참 좋으다. 하지만, 장례를 치뤄주지 않으면, 그냥 좀비가 되어서 내 서버를 잠식하고, 결국에는 서버 전체적으로 fork()가 안되는 매우 위험한 상황까지 발생한다.


그럼 좀비 프로세스는 누가 장례를 치뤄주는가..?

인간 세상과는 다르게 부모가 치뤄준다. 이를 위한 함수가 waitpid(pid_t childpid)이다. waitpid()를 호출하여 해당 childpid의 자식 프로세스가 죽으면 부모 프로세스가 이 함수를 통해서 자식의 main함수의 리턴값을 결과로 받게 되며, 그와 동시에 자식이 가지고 있던 모든 리소스가 해제된다.


그렇다면 몇가지 의문점이 생긴다.

1. 부모가 먼저 죽은 경우는 어찌되느가?

2. 자식이 waitpid()호출전에 죽으면 어찌 되는가?


첫째, 부모가 먼저 죽으면 자식은 고아가 되고, 이 고아 프로세스들을 init 프로세스가 엄마노릇을 대신 하게 된다. 즉, 자식이 살아 있는데 부모가 죽으면, 자식의 부모가 init프로세스로 자동 변경된다.

둘째, 자식이 먼저 죽었는데 부모가 waitpid()로 이를 해제해주지 않으면 일단 좀비 프로세스가 되고, 그후, waitpid()를 호출하는 순간 바로 리턴되면서 자식의 리소스가 해제된다.


#include 
#include 
#include 

int main(int argc, char **argv)
{
    pid_t pid;

    if( (pid = fork()) > 0 )
    {
        while(1);
    }
    else if( pid == 0 )
    {
        return 0;
    }
    else
    {
        int errsv = errno;
        perror("fork error");
        return errsv;
    }

    return 0;
}


위 소스는 fork를 하고, 부모는 무한루프에 빠진 후에, 자식은 바로 죽게 하였다. 그런 후, ps로 상태를 확인하면 다음과 같이 좀비가 되어 있는 자식을 볼 수 있다.



zombie는 프로세스 이름이고, 중간에 보면 Z+ 라고 표시되어 있는데 바로 현재 좀비상태가 되었다는 것을 의미한다. 


그렇다면 좀비를 없애기 위해서 어찌해야 할 것인가? 단순히 waitpid()를 호출하면 장땡인가? 맞다!! 하지만, 문제는 서버도 자식이 죽기를 기다리고만 있는 것을 원하지 않는다는 것이다. 괜히 불렀다가 block되어 버리면 서버는 놀아야 하니까.. 그렇다고 주기적으로 nonblock형태로 불러주는 것도 비효율적이다.


그래서 다음과 같은 시나리오로 zombie 프로세스가 생성되지 않도록 한다!!

  1. A 프로세스는 fork하여 자식 B를 만든다.
  2. 자식 B는 다시 fork하여 자식 C를 만든다.
  3. 자식 B는 바로 죽어 버린다.(ASAP!!) => C는 부모(B)가 죽었으므로 부모가 init로 변경된다. (A와는 전혀 상관 없음)
  4. A가 자식B를 waitpid()로 장례를 치뤄버린다. (정상적으로 종료)

정말 아름다운 기법이 아닐 수 없다!!!

자 예제로 이를 실현해 보도록 하자.


#include 
#include 
#include 

int main(int argc, char **argv)
{
    pid_t pid;

    if( (pid = fork()) > 0 )
    {
        waitpid(pid, NULL, 0);
        while(1);
    }
    else if( pid == 0 )
    {
        if( (pid = fork()) > 0 )
        {
            return 0;
        }
        else if(pid == 0)
        {
            sleep(2);
        }

        return 0;
    }
    else
    {
        int errsv = errno;
        perror("fork error");
        return errsv;
    }

    return 0;
}



이와 같이 했을 경우 ps로 결과를 보면 다음과 같다.


직후



2초후, 손자 프로세스 사망 후


자 깔끔하게 자식 프로세스가 종료되었으며, zombie는 발생하지 않는다..!!





'개발 > Server' 카테고리의 다른 글

[C언어] 쓰레드의 사용  (1) 2012.08.28