注意 第二个例子,使用END块,更完整。
注意 讨论如何使用进程组 因为这是最后。
大多数情况下,您正在处理SIGTERM 信号。为此,您可以安排通过处理程序清理子进程。有些信号不能被捕获,特别是SIGKILL 和SIGSTOP。对于那些你必须去操作系统的人,根据Kaz 的回答。这是其他人的草图。另请参阅下面的其他代码,下面的 cmets,以及最后使用的 进程组。
use warnings;
use strict;
use feature qw(say);
say "Parent pid: $$";
$SIG{TERM} = \&handle_signal; # Same for others that can (and should) be handled
END { say "In END block ..." }
my @kids;
for (1..4) {
push @kids, fork;
if ($kids[-1] == 0) { exec 'sleep 20' }
}
say "Started processes: @kids";
sleep 30;
sub handle_signal {
my $signame = shift;
say "Got $signame. Clean up child processes.";
clean_up_kids(@kids);
die "Die-ing for $signame signal";
};
sub clean_up_kids {
say "\tSending TERM to processes @_";
my $cnt = kill 'TERM', @_;
say "\tNumber of processes signaled: $cnt";
waitpid $_, 0 for @_; # blocking
}
当我以signals.pl & 运行它,然后以kill 运行它时,它会打印出来
[13] 4974
父 pid:4974
已启动流程:4978 4979 4980 4982
提示>杀死4974
有期限。清理子进程。
将 TERM 发送到进程 4978 4979 4980 4982
发出信号的进程数:4
在signals.pl第25行为TERM信号消亡。
在 END 块中...
[13] 出口 4 信号.pl
进程确实被杀死,ps aux | egrep '[s]leep' 在kill 之前和之后检查。
感谢die,END 块被有序执行,因此您可以在那里清理子进程。这样,您还可以免受 die 未捕获的攻击。因此,您只需要使用处理程序来确保 END 块清理发生。
use POSIX "sys_wait_h";
$SIG{CHLD} = sub { while (waitpid(-1, WNOHANG) > 0) { } }; # non-blocking
$SIG{TERM} = \&handle_signal;
END {
clean_up_kids(@kids);
my @live = grep { kill 0, $_ } @kids;
warn "Processes @live still running" if @live;
}
sub clean_up_kids {
my $cnt = kill 'TERM', @_;
say "Signaled $cnt processes.";
}
sub handle_signal { die "Die-ing for " . shift }
在这里,我们在SIGCHLD 处理程序中获取(所有)已终止的子进程,请参阅Signals in perlipc 和waitpid。最后,我们还会检查它们是否都消失了(并收获了)。
kill 0, $pid 即使孩子是僵尸(已退出但未收割)也会返回 true,这可能会在测试中发生,因为父母会在之后检查 right。如果需要,在clean_up_kids() 之后添加sleep 1。
一些笔记。这远不是要考虑的完整列表。除了提到的(和其他)Perl 文档,还可以查看 UNIX 和 C 文档,因为 Perl 的 ipc 是直接基于 UNIX 系统工具构建的。
实际上这里省略了所有错误检查。请添加
等待特定进程是阻塞的,因此如果某些进程没有被终止,程序将挂起。非阻塞 waitpid 有另一个警告,请参阅链接的 perlipc 文档
子进程可能在父进程被杀死之前已经退出。 kill 'TERM' 发送 SIGTERM 但这并不能确保孩子终止。流程可能存在也可能不存在
信号处理程序可能会在END 阶段失效,请参阅this post。在我的测试中,CHLD 仍然在此处处理,但如果这是一个问题,请重新安装处理程序,如链接答案中所示
有用于这方面各个方面的模块。参见sigtrap pragma 示例
建议不要在信号处理程序中做太多事情
发生了很多事情,错误可能会产生意想不到的深远影响
如果您kill 进程组,您将不会遇到任何这些问题,因为所有子进程也会被终止。在我的系统上,这可以通过
在终端完成
提示> kill -s TERM -pid
您可能必须使用数字信号,通常15 对应TERM,请参阅您系统上的man kill。 -pid 代表进程组,用减号表示。编号与进程ID相同,但在代码中添加say getpgrp;即可查看。如果这个进程不是简单地由 shell 启动的,而是从另一个脚本启动的,它将属于它的父进程组,它的子进程也是如此。然后你需要先设置它自己的进程组,它的子进程会继承,然后你可以kill那个进程组。请参阅setpgrp 和getpgrp。