Working With Processes and Signals
한국어 원문으로 표시합니다.
목표
프로세스에 시그널을 보내며 정지·종료·강제 종료가 각각 무엇이 다른지 확인하고, 좀비가 어떻게 생기는지 직접 만들어 봅니다.
왜 중요한가
"프로세스가 안 죽는다"는 신고는 최소 네 가지 다른 상황을 뭉뚱그린 말입니다. 애플리케이션이 SIGTERM 을 잡아 무시했을 수도 있고, D 상태라 시그널 처리 자체가 미뤄졌을 수도 있고, 이미 죽어서 좀비로 남은 것일 수도 있습니다. 대응이 전부 다르기 때문에 어느 계층에서 멈췄는지를 먼저 물어야 합니다. 그리고 SIGKILL 과 SIGSTOP 만 잡을 수 없다는 규칙은 커널이 운영자에게 남겨 둔 최후의 수단입니다 - 그래서 SIGKILL 은 정리 기회를 주지 않고, DB 나 큐 소비자에게는 전원을 뽑는 것과 같습니다.
단계
sleep 3600을 백그라운드로 띄우고 그 PID 를/root/proc/pid.txt에 적으세요.- 그 프로세스의 부모 PID 를
/root/proc/ppid.txt에 적으세요. - 또 다른
sleep을 띄워 정지(SIGSTOP) 시키고, PID 를/root/proc/stopped_pid.txt에, 정지 후의 상태 글자 한 개를/root/proc/state_stopped.txt에 적으세요. - 또 다른
sleep을 띄워 PID 를/root/proc/killed_pid.txt에 적은 뒤 SIGTERM 으로 종료시키세요. /root/proc/trapper.sh를 만드세요. SIGTERM 을 받으면/root/proc/term.log에caught SIGTERM한 줄을 추가해야 합니다. 실행 권한을 주고 백그라운드로 띄운 뒤 SIGTERM 을 보내 로그가 쌓이는지 확인하세요.trapper.sh를 다시 띄우고 이번엔 SIGKILL 을 보내세요. 보내기 직전term.log의 줄 수를/root/proc/before_kill.txt에, 보낸 직후의 줄 수를/root/proc/after_kill.txt에 적으세요. 실습이 끝나면trapper.sh는 하나도 살아 있으면 안 됩니다.- 자식을 하나 낳고 그 자식이 먼저 끝나도록 하되, 부모는
wait하지 않고 계속 살아 있게 만드세요. 그 부모의 PID 를/root/proc/zombie_parent.txt에 적으세요. /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 를 제외하지 않으면 스크립트가 스스로를 죽입니다.
백그라운드 프로세스 띄우기
sleep 3600 을 백그라운드로 띄우고 그 PID 를 /root/proc/pid.txt 에 적으세요.
명령 뒤에 & 를 붙이면 백그라운드로 갑니다. 바로 다음에 $! 안에 그 PID 가 들어 있습니다.
부모 PID 확인하기
그 프로세스의 부모 PID 를 /root/proc/ppid.txt 에 적으세요.
/proc//status 의 PPid 줄을 보거나 ps -o ppid= -p 를 씁니다.
프로세스 정지시키기
또 다른 sleep 을 띄워 정지(SIGSTOP) 시키고, PID 를 /root/proc/stopped_pid.txt 에, 정지 후의 상태 글자 한 개를 /root/proc/state_stopped.txt 에 적으세요.
정지와 종료는 다릅니다. 정지된 프로세스는 목록에 남아 있고 상태 글자가 바뀝니다.
정중하게 종료 요청하기
또 다른 sleep 을 띄워 PID 를 /root/proc/killed_pid.txt 에 적은 뒤 SIGTERM 으로 종료시키세요.
kill 은 기본적으로 SIGTERM 을 보냅니다. 보내기 전에 PID 를 파일에 적어 두세요.
SIGTERM 을 잡는 스크립트
/root/proc/trapper.sh 를 만드세요. SIGTERM 을 받으면 /root/proc/term.log 에 caught SIGTERM 한 줄을 추가해야 합니다. 실행 권한을 주고 백그라운드로 띄운 뒤 SIGTERM 을 보내 로그가 쌓이는지 확인하세요.
trap '명령' TERM 형태로 핸들러를 겁니다. 스크립트는 종료되지 않고 계속 살아 있어야 신호를 받을 수 있습니다.
SIGKILL 은 잡히지 않는다
trapper.sh 를 다시 띄우고 이번엔 SIGKILL 을 보내세요. 보내기 직전 term.log 의 줄 수를 /root/proc/before_kill.txt 에, 보낸 직후의 줄 수를 /root/proc/after_kill.txt 에 적으세요. 실습이 끝나면 trapper.sh 는 하나도 살아 있으면 안 됩니다.
trap 이 걸린 그 스크립트에 9번을 보내고, 로그 줄 수가 늘었는지 전후로 비교해 기록하세요.
좀비 만들어 보기
자식을 하나 낳고 그 자식이 먼저 끝나도록 하되, 부모는 wait 하지 않고 계속 살아 있게 만드세요. 그 부모의 PID 를 /root/proc/zombie_parent.txt 에 적으세요.
자식이 먼저 끝나고 부모가 wait 하지 않은 채 살아 있으면 좀비가 됩니다. 부모가 계속 살아 있어야 합니다.
패턴 기반 정리 스크립트
/root/proc/cleanup.sh <패턴> 을 만드세요. 명령줄이 그 패턴에 맞는 프로세스를 모두 종료하고, 종료한 개수를 terminated=<n> 형식으로 한 줄 출력해야 합니다. 자기 자신은 세지도 죽이지도 않아야 합니다.
pgrep -f 로 먼저 세고, 자기 자신은 제외해야 합니다. 종료 후 개수를 terminated= 형식으로 출력하세요.