리눅스 kill 명령어 사용법 - 시그널 종류와 -9를 쓰면 안 되는 이유
이 명령어를 언제 쓰는가
이름 때문에 "프로세스를 죽이는 명령"으로 알려져 있지만, 정확히는 프로세스에 시그널을 보내는 명령이다. 종료는 그중 한 용도일 뿐이다.
실무에서 kill을 쓰는 상황은 크게 셋이다. 응답이 없는 프로세스를 정리할 때, 데몬에게 설정 파일을 다시 읽으라고 지시할 때(HUP), 그리고 배포 스크립트에서 이전 버전을 안전하게 내릴 때다. 세 번째가 가장 자주 쓰이고, 여기서 사고도 가장 많이 난다.
기본 형식
kill [-시그널] PID [PID...]
시그널은 번호(-9)로도 이름(-KILL 또는 -s KILL)으로도 지정한다. 이름 쪽이 읽기 좋아서 스크립트에는 이름을 권한다. 시그널을 생략하면 TERM이다. 사용 가능한 시그널 목록은 kill -l로 확인한다.
자주 쓰는 시그널
| 시그널 | 의미 | 예시 |
|---|---|---|
| TERM (15) | 기본값. "정리하고 종료해라". 애플리케이션이 가로채서 뒷정리할 수 있다 | kill 21874 |
| HUP (1) | 원래는 터미널 끊김. 관례적으로 데몬의 설정 리로드에 쓴다 | kill -HUP 1180 |
| INT (2) | Ctrl+C와 같은 인터럽트 | kill -INT 21874 |
| QUIT (3) | 종료하며 코어 덤프. JVM은 스레드 덤프를 남긴다 | kill -QUIT 21874 |
| KILL (9) | 커널이 강제로 제거. 프로세스가 가로챌 수 없다 | kill -9 21874 |
| STOP (19) | 일시 정지. 상태는 유지된다 | kill -STOP 21874 |
| CONT (18) | 정지된 프로세스 재개 | kill -CONT 21874 |
| USR1 / USR2 | 애플리케이션이 의미를 정한다. nginx는 USR1이 로그 파일 재오픈 | kill -USR1 1180 |
| 0 | 시그널을 보내지 않고 존재 여부와 권한만 확인 | kill -0 21874 |
실전 예제
1) 정상 종료를 시도하고, 안 되면 강제 종료한다. 배포 스크립트에서 이전 프로세스를 내릴 때 쓰는 표준 패턴이다. 무작정 -9부터 치지 않는 것이 핵심이다.
PID=$(cat /var/run/myapp.pid)
kill "$PID"
for i in $(seq 1 15); do
kill -0 "$PID" 2>/dev/null || break
sleep 1
done
kill -0 "$PID" 2>/dev/null && kill -9 "$PID"
2) 무중단으로 nginx 설정을 반영한다. 재시작 없이 워커를 교체하므로 처리 중인 요청이 끊기지 않는다. 반드시 문법 검사를 먼저 한다.
nginx -t && kill -HUP $(cat /var/run/nginx.pid)
3) 로그 로테이션 후 로그 파일을 다시 열게 한다. 이걸 빼먹으면 프로세스가 이름이 바뀐 옛 파일에 계속 쓴다.
kill -USR1 $(cat /var/run/nginx.pid)
4) Java 프로세스의 스레드 덤프를 뜬다. 애플리케이션은 죽지 않고, 표준 출력(보통 catalina.out이나 서비스 로그)에 전체 스레드 스택이 찍힌다. 응답 없는 원인을 찾을 때 쓴다.
kill -QUIT 21874
5) 자식까지 통째로 정리한다. PID 앞에 마이너스를 붙이면 프로세스 그룹 전체가 대상이 된다. 셸 스크립트가 띄운 자식들이 고아로 남는 걸 막는다.
kill -TERM -21874
함정과 주의점
kill -9를 습관처럼 쓰면 언젠가 데이터가 깨진다. KILL은 프로세스가 가로챌 수 없으므로 버퍼를 디스크에 내리지도, 락 파일을 지우지도, 임시 파일을 치우지도 못한다. MySQL이나 Redis를 -9로 내리면 다음 기동 때 크래시 복구가 돌고, 최악의 경우 마지막 트랜잭션이 날아간다. PID 파일이 남아 서비스가 "이미 실행 중"이라며 안 뜨는 것도 전형적인 후유증이다. 항상 TERM을 먼저 보내고 몇 초 기다린다.
-9로도 안 죽는 프로세스가 있다. ps의 STAT이 D인 프로세스는 커널 안에서 인터럽트 불가능한 I/O를 기다리는 중이라 어떤 시그널도 전달되지 않는다. 대개 NFS 마운트가 끊겼거나 디스크가 고장난 경우다. 프로세스를 죽이는 게 아니라 원인(스토리지)을 해결해야 한다. STAT이 Z(좀비)인 경우도 kill이 안 통한다. 이미 죽은 프로세스이므로 부모를 정리해야 사라진다.
PID는 재사용된다. 몇 시간 전에 만들어진 PID 파일을 그대로 믿고 kill -9 $(cat old.pid)를 치면, 그 사이 같은 번호를 물려받은 전혀 다른 프로세스가 죽을 수 있다. 죽이기 전에 ps -p "$PID" -o cmd=로 명령줄이 기대한 것과 맞는지 확인하는 습관을 들여야 한다.
kill %1은 셸 빌트인에서만 동작한다. %1 같은 잡 번호는 bash가 해석하는 표기라, /bin/kill이나 env kill로 우회하면 인식하지 못한다.
함께 보면 좋은 명령어
- pkill / pgrep — PID를 몰라도 이름이나 사용자로 시그널을 보낸다. 대신 범위가 넓어 위험하다.
- systemctl — 서비스로 등록된 프로세스는
systemctl stop이 정답이다. kill로 내리면 유닛 상태와 어긋난다. - timeout — 명령에 제한 시간을 걸어 알아서 시그널을 보내게 한다. 걸릴 수 있는 배치를 돌릴 때 유용하다.