리눅스 시스템을 top 명령어로 모니터링하다 보면, '작업 (Tasks)' 라인에 1 좀비 (zombie)와 같은 항목을 발견할 때가 있습니다.
top - 16:03:19 up 7:09, 1 user, load average: 2.24, 2.21, 2.29
작 업: 392 총계, 1 실행, 390 대기, 0 멈춤, 1 좀비
...
이 '좀비'는 무엇이며, 시스템에 어떤 영향을 미칠까요? 이 글에서는 좀비 프로세스(Zombie Process)의 정체와 이를 확인하고 해결하는 방법을 명확하게 설명합니다.
좀비 프로세스(Zombie Process)란 무엇인가? 🧟
이해하기 쉽게 프로세스의 생명 주기에 비유해 보겠습니다.
-
탄생 (Fork): 부모 프로세스(Parent Process)가 자식 프로세스(Child Process)를 생성합니다 (
fork()). -
실행 (Exec): 자식 프로세스가 자신의 작업을 수행합니다.
-
종료 (Exit): 자식 프로세스가 작업을 완료하고 종료합니다 (
exit()). -
수확 (Wait): 자식 프로세스가 종료되면, 운영체제(커널)는 해당 프로세스의 PID, 종료 상태 등의 정보를 프로세스 테이블에 남겨둡니다. 그리고 부모 프로세스에게
SIGCHLD(자식이 종료되었음) 시그널을 보냅니다. -
부모 프로세스는 이 시그널을 받고
wait()시스템 콜을 호출하여 자식의 종료 상태 정보를 "수확"해가야 합니다. 이 정보가 수확되면, 커널은 비로소 프로세스 테이블에서 자식의 항목을 완전히 제거합니다.
좀비 프로세스는 바로 4번과 5번 사이의 상태에 갇힌 프로세스입니다. 즉, 자식 프로세스는 실행을 완료하고 종료했지만, 부모 프로세스가 아직 wait()를 호출하여 종료 상태를 수확해가지 않은 상태입니다.
이름처럼 이 프로세스는 죽어있는 상태(실행 중이 아님)입니다. 따라서 CPU나 메모리 같은 시스템 자원을 소모하지는 않습니다.
좀비 프로세스는 왜 문제가 되나요?
좀비 프로세스 자체는 시스템 자원을 거의 사용하지 않지만, 프로세스 테이블의 한 슬롯(PID)을 차지합니다.
만약 부모 프로세스의 버그 등으로 인해 좀비 프로세스가 계속해서 쌓이고 정리되지 않는다면, 시스템이 할당할 수 있는 PID 개수(최대값)에 도달할 수 있습니다. 이 경우, 시스템은 더 이상 새로운 프로세스를 생성하지 못하게 되어 심각한 장애로 이어질 수 있습니다. top에서 1~2개의 좀비가 보이는 것은 드문 일이 아니지만, 이 숫자가 계속 증가한다면 조치가 필요합니다.
좀비 프로세스 확인 및 식별 방법
top 명령어는 좀비의 _개수_만 보여줍니다. 어떤 프로세스가 좀비 상태인지, 그리고 그 부모는 누구인지 확인하려면 ps 명령어를 사용해야 합니다.
가장 간단하게는 ps 명령어의 STAT (상태) 열에서 'Z'로 표시된 프로세스를 찾으면 됩니다.
# 시스템의 모든 프로세스를 상세히 보면서 'Z' 상태(좀비)인 것을 필터링
ps -elf | grep ' Z '
# 또는 'aux' 옵션을 사용 (8번째 컬럼($8)이 상태(STAT)임)
ps aux | awk '$8=="Z"'
예시 출력 :
# ps -elf | grep ' Z '
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
0 Z user 5021 5000 0 80 0 - 0 exit 15:30 ? 00:00:00 [defunct]
위 예시에서 중요한 정보는 다음과 같습니다.
-
S (State):
Z(좀비 상태임을 의미) -
PID:
5021(이것이 좀비 프로세스의 PID입니다) -
PPID:
5000(이것이 좀비를 수확하지 않은 부모 프로세스의 PID입니다) -
CMD:
[defunct](종료되었으나 정리되지 않았음을 나타내는 이름)
좀비 프로세스 해결 방법
가장 중요한 사실은 좀비 프로세스는 kill 명령어로 죽일 수 없다는 것입니다.
kill -9 5021(위 예시의 좀비 PID)
위 명령어는 작동하지 않습니다. 좀비는 이미 "죽어있는" 상태이기 때문입니다. kill 시그널을 처리할 주체가 없습니다.
좀비 프로세스를 해결하는 유일한 방법은 부모 프로세스가 wait()를 호출하도록 하는 것입니다.
1단계: 부모 프로세스에 시그널 보내기 (권장)
가장 먼저 시도할 방법은 부모 프로세스(PPID)에게 자식의 상태를 확인하라고 SIGCHLD 시그널을 수동으로 보내는 것입니다.
# 위 예시의 부모 PID(5000)에게 SIGCHLD 시그널 전송
kill -s SIGCHLD 5000
이는 부모 프로세스에 "네 자식 프로세스 중 하나가 종료되었으니 확인해봐!"라고 알려주는 효과가 있습니다. 정상적으로 프로그래밍된 부모라면 이 시그널을 받고 좀비를 수확하여 정리합니다.
2단계: 부모 프로세스 강제 종료 (최후의 수단)
만약 1단계가 통하지 않는다면, 이는 부모 프로세스(PPID 5000) 자체가 멈췄거나, wait()를 호출하는 로직에 심각한 버그가 있다는 의미입니다.
이 경우, 부모 프로세스를 강제로 종료하는 것이 유일한 해결책입니다.
# 부모 프로세스(PPID 5000)를 종료
kill 5000
# 그래도 종료되지 않으면 강제 종료
kill -9 5000
왜 부모를 죽이면 해결될까요?
리눅스에서 부모 프로세스가 죽으면, 그 자식 프로세스들(고아 프로세스)은 자동으로 init 프로세스(PID 1) 또는 systemd 에 의해 입양됩니다. init 프로세스는 주기적으로 자식들의 상태를 확인하고 종료된 자식(좀비 포함)을 즉시 수확(reap)하도록 설계되어 있습니다.
따라서 말썽꾸러기 부모(PPID 5000)가 죽으면, 좀비(PID 5021)는 init의 새 자식이 되고, init이 즉시 좀비를 정리해줍니다.
⚠️ 주의: 부모 프로세스를 종료하기 전,
ps -p 5000(부모 PID) 명령을 통해 해당 프로세스가 시스템의 중요 서비스(예: DB, 웹서버 등)가 아닌지 반드시 확인해야 합니다. 중요한 서비스를 강제로 종료하면 더 큰 장애가 발생할 수 있습니다.
요약
-
좀비 프로세스는 실행이 끝났지만 부모가 종료 상태를 수확하지 않은, 프로세스 테이블에 남은 찌꺼기입니다.
-
자원을 소모하진 않지만, PID를 점유하며, 과도하게 많아지면 시스템 장애를 유발합니다.
-
ps -elf | grep ' Z '명령으로 좀비(PID)와 그 부모(PPID)를 찾을 수 있습니다. -
해결책은 좀비가 아닌 부모 프로세스(PPID) 를 대상으로 합니다.
-
kill -s SIGCHLD <부모PID>(권장) -
kill <부모PID>(최후의 수단)
-
댓글이 없습니다.