리눅스 기초 · 프로세스와 신호 · 실습
프로세스와 시그널 다루기
목표
프로세스에 시그널을 보내며 정지·종료·강제 종료가 각각 무엇이 다른지 확인하고, 좀비가 어떻게 생기는지 직접 만들어 봅니다.
왜 중요한가
"프로세스가 안 죽는다"는 신고는 최소 네 가지 다른 상황을 뭉뚱그린 말입니다. 애플리케이션이 SIGTERM 을 잡아 무시했을 수도 있고, D 상태라 시그널 처리 자체가 미뤄졌을 수도 있고, 이미 죽어서 좀비로 남은 것일 수도 있습니다. 대응이 전부 다르기 때문에 어느 계층에서 멈췄는지를 먼저 물어야 합니다. 그리고 SIGKILL 과 SIGSTOP 만 잡을 수 없다는 규칙은 커널이 운영자에게 남겨 둔 최후의 수단입니다 - 그래서 SIGKILL 은 정리 기회를 주지 않고, DB 나 큐 소비자에게는 전원을 뽑는 것과 같습니다.
단계
1. sleep 3600 을 백그라운드로 띄우고 그 PID 를 /root/proc/pid.txt 에 적으세요.
2. 그 프로세스의 부모 PID 를 /root/proc/ppid.txt 에 적으세요.
3. 또 다른 sleep 을 띄워 정지(SIGSTOP) 시키고, PID 를 /root/proc/stopped_pid.txt 에, 정지 후의 상태 글자 한 개를 /root/proc/state_stopped.txt 에 적으세요.
4. 또 다른 sleep 을 띄워 PID 를 /root/proc/killed_pid.txt 에 적은 뒤 SIGTERM 으로 종료시키세요.
5. /root/proc/trapper.sh 를 만드세요. SIGTERM 을 받으면 /root/proc/term.log 에 caught SIGTERM 한 줄을 추가해야 합니다. 실행 권한을 주고 백그라운드로 띄운 뒤 SIGTERM 을 보내 로그가 쌓이는지 확인하세요.
6. trapper.sh 를 다시 띄우고 이번엔 SIGKILL 을 보내세요. 보내기 직전 term.log 의 줄 수를 /root/proc/before_kill.txt 에, 보낸 직후의 줄 수를 /root/proc/after_kill.txt 에 적으세요. 실습이 끝나면 trapper.sh 는 하나도 살아 있으면 안 됩니다.
7. 자식을 하나 낳고 그 자식이 먼저 끝나도록 하되, 부모는 wait 하지 않고 계속 살아 있게 만드세요. 그 부모의 PID 를 /root/proc/zombie_parent.txt 에 적으세요.
8. /root/proc/cleanup.sh <패턴> 을 만드세요. 명령줄이 그 패턴에 맞는 프로세스를 모두 종료하고, 종료한 개수를 terminated=<n> 형식으로 한 줄 출력해야 합니다. 자기 자신은 세지도 죽이지도 않아야 합니다.
참고
awk '{print $3}' /proc/<PID>/stat로 상태 글자를 볼 수 있습니다. R 실행, S 대기, D 중단 불가 대기, T 정지, Z 좀비.kill -0 <PID>는 시그널을 보내지 않고 존재·권한만 확인합니다. 생존 폴링의 정석입니다.- 흔한 실수 1: 3번에서 종료(TERM)와 정지(STOP)를 헷갈리면 프로세스가 사라져 채점이 실패합니다.
- 흔한 실수 2: 8번에서
pgrep -f는 자기 자신의 명령줄에도 걸립니다. 자기 PID 를 제외하지 않으면 스크립트가 스스로를 죽입니다.
단계 8개
- 백그라운드 프로세스 띄우기
- 부모 PID 확인하기
- 프로세스 정지시키기
- 정중하게 종료 요청하기
- SIGTERM 을 잡는 스크립트
- SIGKILL 은 잡히지 않는다
- 좀비 만들어 보기
- 패턴 기반 정리 스크립트