리눅스 시스템을 `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 좀비 ... ``` ![시스템엔지니어가 좀비를 발견하고 당혹스러워하는 유머스러운 이미지](/media/editor_temp/6/c423a395-6a79-400b-b4a8-f4b0c3e69225.png) 이 '좀비'는 무엇이며, 시스템에 어떤 영향을 미칠까요? 이 글에서는 좀비 프로세스(Zombie Process)의 정체와 이를 확인하고 해결하는 방법을 명확하게 설명합니다. --- 좀비 프로세스(Zombie Process)란 무엇인가? 🧟 -------------------------------- 이해하기 쉽게 프로세스의 생명 주기에 비유해 보겠습니다. 1. **탄생 (Fork):** 부모 프로세스(Parent Process)가 자식 프로세스(Child Process)를 생성합니다 (`fork()`). 2. **실행 (Exec):** 자식 프로세스가 자신의 작업을 수행합니다. 3. **종료 (Exit):** 자식 프로세스가 작업을 완료하고 종료합니다 (`exit()`). 4. **수확 (Wait):** 자식 프로세스가 종료되면, 운영체제(커널)는 해당 프로세스의 PID, 종료 상태 등의 정보를 **프로세스 테이블**에 남겨둡니다. 그리고 부모 프로세스에게 `SIGCHLD` (자식이 종료되었음) 시그널을 보냅니다. 5. 부모 프로세스는 이 시그널을 받고 `wait()` 시스템 콜을 호출하여 자식의 종료 상태 정보를 "수확"해가야 합니다. 이 정보가 수확되면, 커널은 비로소 프로세스 테이블에서 자식의 항목을 완전히 제거합니다. **좀비 프로세스**는 바로 4번과 5번 사이의 상태에 갇힌 프로세스입니다. 즉, **자식 프로세스는 실행을 완료하고 종료했지만, 부모 프로세스가 아직 `wait()`를 호출하여 종료 상태를 수확해가지 않은 상태**입니다. 이름처럼 이 프로세스는 **죽어있는 상태(실행 중이 아님)**입니다. 따라서 CPU나 메모리 같은 시스템 자원을 소모하지는 않습니다. ### 좀비 프로세스는 왜 문제가 되나요? {#sec-e0c2ef2b5681} 좀비 프로세스 자체는 시스템 자원을 거의 사용하지 않지만, 프로세스 테이블의 한 슬롯(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단계: 부모 프로세스에 시그널 보내기 (권장) {#sec-780f28469b5d} 가장 먼저 시도할 방법은 부모 프로세스(PPID)에게 자식의 상태를 확인하라고 `SIGCHLD` 시그널을 수동으로 보내는 것입니다. ``` # 위 예시의 부모 PID(5000)에게 SIGCHLD 시그널 전송 kill -s SIGCHLD 5000 ``` 이는 부모 프로세스에 "네 자식 프로세스 중 하나가 종료되었으니 확인해봐!"라고 알려주는 효과가 있습니다. 정상적으로 프로그래밍된 부모라면 이 시그널을 받고 좀비를 수확하여 정리합니다. ### 2단계: 부모 프로세스 강제 종료 (최후의 수단) {#sec-d338bbe4ae7b} 만약 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)** 를 대상으로 합니다. 1. `kill -s SIGCHLD <부모PID>` (권장) 2. `kill <부모PID>` (최후의 수단)