OS Programme Lecture #6
1. Propagate the signal to multiple children
接着上节课的进程组脚本程序,我们尝试将信号传播给多个子进程
之前,我们使用一个只有一个子进程的脚本。如果一个脚本有很多子进程,并且其中一些还有自己的子进程(进程树形成),会是什么情况?
在第一种情况下,获取所有子作业的 pids 的一种快速方法是使用 jobs -p 命令:
该命令显示当前 shell 中所有活动作业的 pids,我们可以使用 kill 来终止它们。
参考程序:
#!/bin/bash
cleanup() {
echo "cleaning up..."
# Our cleanup code goes here
}
trap 'echo signal received!; kill $(jobs -p); wait; cleanup' SIGINT SIGTERM
echo "The script pid is $"
sleep 30 &
sleep 40 &
wait
该脚本在后台启动两个进程:通过使用不带参数的内置等待,我们等待所有进程,并保持父进程处于活动状态。
当脚本接收到 SIGINT 或 SIGTERM 信号时,我们向它们发送一个 SIGTERM,让它们的 pid 由 jobs -p 命令返回(job 本身是一个内置的 shell,所以当我们使用它时,一个新进程未创建)。
如果子进程有自己的子进程,并且我们想在父进程接收到信号时终止它们,我们可以向整个进程组发送一个信号,就像我们之前看到的那样。
然而,这会带来一个问题,因为通过向进程组发送终止信号,我们将进入“信号发送/信号捕获”循环。
想一想:在 SIGTERM 的陷阱中,我们向进程组的所有成员发送一个 SIGTERM 信号;这包括父脚本本身!
为了解决这个问题并在子进程终止后仍然能够执行清理功能,我们必须在向进程组发送信号之前更改 SIGTERM 的陷阱。
参考程序:
#!/bin/bash
cleanup() {
echo "cleaning up..."
# Our cleanup code goes here
}
trap 'trap " " SIGTERM; kill 0; wait; cleanup' SIGINT SIGTERM
echo "The script pid is $"
sleep 30 &
sleep 40 &
wait
在trap中,在向进程组发送SIGTERM之前,我们改变了SIGTERM trap,让父进程忽略这个信号,只有子进程受到影响。
另外,在陷阱中,为了向进程组发出信号,我们使用 kill 并将 0 作为 pid。
这是一种捷径:当传递给 kill 的 pid 为 0 时,当前进程组中的所有进程都会收到信号。
总结一下:
我们了解了进程组以及前台进程和后台进程之间的区别。
我们学习了 CTRL-C 向控制终端的整个前台进程组发送 SIGINT 信号。
我们学习了如何使用 kill 向进程组发送信号。
我们还学习了如何在后台执行程序,以及如何使用内置的 wait 等待它退出而不丢失父进程。
最后,我们看到了如何设置一个脚本,以便当它接收到一个信号时,它会在退出之前终止它的子进程。
SIGINT
产生方式: 键盘Ctrl+C
产生结果: 只对当前前台进程,和他的所在的进程组的每个进程都发送SIGINT信号,之后这些进程会执行信号处理程序再终止.
SIGTERM
产生方式: 和任何控制字符无关,用kill函数发送
本质: 相当于shell> kill不加-9时 pid.
产生结果: 当前进程会收到信号,而其子进程不会收到.如果当前进程被kill(即收到SIGTERM),则其子进程的父进程将为init,即pid为1的进程.
与SIGKILL的不同: SIGTERM可以被阻塞,忽略,捕获,也就是说可以进行信号处理程序,那么这样就可以让进程很好的终止,允许清理和关闭文件.
SIGKILL
产生方式: 和任何控制字符无关,用kill函数发送
本质: 相当于shell> kill -9 pid.
产生结果: 当前进程收到该信号,注意该信号时无法被捕获的,也就是说进程无法执行信号处理程序,会直接发送默认行为,也就是直接退出.这也就是为何kill -9 pid一定能杀死程序的原因. 故这也造成了进程被结束前无法清理或者关闭资源等行为,这样时不好的.
注意
由于SIGINT, SIGTERM都是可以被捕获的,也就是会执行信号处理函数的,故按照信号处理函数逻辑,可能进程不会退出,即不一定能终止,所以要处理好exit(0).
————————————————
版权声明:本文为CSDN博主「flye422304」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/flye422304/article/details/116890946
####################################################
2. 再来看一个任务,有关Parent and Children Processes
写一个脚本程序 loop_pid_s.sh,可以接收两个参数 ARG1 (进程名,例如PA)、ARG2 (脚本运行时间)。
这个脚本相隔一定时间间隔打印一句话,要用到上面学到的sleep来控制脚本输出。
参考代码:
#!/bin/bash
let time=1
while true; do
echo "$$ I am process $1 - $time-seconds"
sleep $2
let time+=$2
done
完成脚本后,尝试运行传入不同的参数,查看运行结果。尝试用两种方式终止进程。
下面写一个parent.sh脚本程序,参考代码:
#!/bin/bash
(./loop_pid_s.sh PA 10)
(./loop_pid_s.sh PB 10)
echo "Program terminated ..."
运行以上脚本,观察输出。思考:PA和PB两个进程是否并行执行?什么时候最后一个句柄会打印到终端?
现在修改parent.sh脚本程序,参考代码:
#!/bin/bash
(./loop_pid_s.sh PA 10 &)
(./loop_pid_s.sh PB 10 &)
echo "Program terminated ..."
运行以上脚本,比较和之前的输出,思考为什么不一样。
仅供初学者参考学习,除去中间部分已经标明出处的内容外,版权归广大所有