操作进程与信号
目标
向进程发送信号,确认暂停、终止、强制终止之间有何不同,并亲手制造僵尸进程,观察它如何产生。
为什么重要
‘进程杀不掉’这种报告,至少把四种不同情况混为一谈:应用程序可能捕获并忽略了 SIGTERM;进程可能处于 D 状态,导致信号处理本身被延后;也可能早已结束,只是作为僵尸进程残留下来。每种情况的处理方式完全不同,因此首先要问清楚停在哪一层。此外,只有 SIGKILL 和 SIGSTOP 无法被捕获,这是内核留给管理员的最后手段——因此 SIGKILL 不会给程序清理的机会,对于数据库或队列消费者而言,等同于直接拔掉电源。
步骤
- 在后台启动
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= 格式输出数量。