当父母被杀时,Perl是否有一种简单的方法可以杀死子进程?当我在父PID上运行kill
时,孩子们就会活着。
测试脚本:
#!/usr/bin/perl
@sleeps = qw( 60 70 80 );
@pids = ();
foreach $sleeptime (@sleeps) {
my $pid = fork();
if ( not defined $pid ) {
die;
}
elsif ( $pid == 0 ) {
#child
system("sleep $sleeptime");
exit;
}
else {
#parent
push @pids, $pid;
}
}
foreach $pid (@pids) {
waitpid( $pid, 0 );
}
答案 0 :(得分:6)
注意使用END
块的第二个示例更完整
注意最后,有关如何使用进程组的示例。
大部分时间都是您处理SIGTERM
信号的机会。为此,您可以通过处理程序安排清理子进程。有些信号无法被捕获,特别是SIGKILL
和SIGSTOP
。对于那些你必须转到操作系统的人,每个答案都是Kaz。这是其他人的草图。另请参阅下面的其他代码,下面的评论以及最后使用的进程组。
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 Parent pid: 4974 Started processes: 4978 4979 4980 4982 prompt> kill 4974 Got TERM. Clean up child processes. Sending TERM to processes 4978 4979 4980 4982 Number of processes signaled: 4 Die-ing for TERM signal at signals.pl line 25. In END block ... [13] Exit 4 signals.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,因此在测试中可能会发生这种情况,因为父母会在之后检查正确。如果需要,请在sleep 1
之后添加clean_up_kids()
。
一些笔记。这远不是要考虑的完整列表。除了提到的(和其他)Perl文档之外,还可以看到UNIX和C文档,因为Perl的ipc是直接在UNIX系统工具上构建的。
这里几乎省略了所有错误检查。请添加。
等待特定进程阻塞,因此如果某些进程未终止,程序将挂起。非阻止waitpid
还有另一个警告,请参阅链接的perlipc
文档。
在父母被杀之前,子进程可能已经退出。 kill 'TERM'
发送SIGTERM
,但这并不能确保子项终止。流程可能存在也可能不存在。
信号处理程序可能会在END
阶段失效,请参阅this post。在我的测试中,CHLD
仍在此处理,但如果这是一个问题,请重新安装处理程序,如链接的答案。
有各方面的模块。例如,请参阅sigtrap pragma。
建议不要在信号处理程序中做太多工作。
有很多事情发生,错误会产生意想不到的后果。
如果您{{1>} 进程组,您将不会遇到任何这些问题,因为所有子项也会被终止。在我的系统上,这可以通过
在终端完成prompt> kill -s TERM -pid
您可能必须使用数字信号,kill
通常为15
,请参阅系统上的TERM
。 man kill
代表进程组,由减号表示。该数字与进程ID相同,但将-pid
添加到代码中以查看。如果shell没有简单地启动这个过程,而是从另一个脚本说,它将属于其父进程组,其子进程也是如此。然后,您需要首先设置自己的进程组,其子进程将继承,然后您可以say getpgrp;
那个进程组。请参阅setpgrp和getpgrp,然后搜索SO帖子。
答案 1 :(得分:2)
这是必须在操作系统上完成的,以便可靠。 Linux有一个prctl
(进程控制)系统调用,它复用了许多函数,类似于ioctl
。其中一个函数(操作码PR_SET_PDEATHSIG
)安排进程在父进程死亡时接收特定信号(作为第二个参数传递)。
有一个CPAN module收尾prctl
,其中显示为set_pdeathsig
函数,其中包含信号编号。
如果没有prctl
,子进程(初始进程没有生成,因此最初具有1以外的父进程ID)可以定期测试getppid()
的值。如果改为1,则意味着其原始父母死亡。
管理孩子的进程也可以实现优雅的关闭逻辑:捕获终止信号并终止他们的孩子作为清理的一部分。当然,对于无法处理的信号来说,这是不可能的。
答案 2 :(得分:-3)
以下内容如何:
kill( -1 * getpgid(), 9 );
(9是SIGKILL:不需要宏。)