我正在Linux主机上运行perl脚本。我正在尝试编写一个分叉的脚本,其中孩子启动一个永远需要的程序,父母在5秒后超时,杀死了孩子。这就是我所拥有的:
my $start = time();
my $timeOut = 5;
my $childPid = fork();
if ($childPid) {
# I am the parent, and $childPid is my child
while (kill(0, $childPid)) {
if (time() > $start + $timeOut) {
$numKilled = kill(SIGTERM, $childPid);
printf("numKilled: %s\n", $numKilled);
}
sleep 1;
}
}
else {
# I am the child - do something that blocks forever
`adb -s 410bf6c1 logcat`;
exit;
}
输出:
aschirma@graphics9-lnx:~$ perl perl-fork-test.pl
numKilled: 1
numKilled: 1
numKilled: 1
numKilled: 1
numKilled: 1
...
我期望的行为是我看到“numKilled:1”恰好一次,并且子进程(及其任何子进程)在大约5秒后被杀死。但我从实验中看到,孩子和孩子都没有被杀。 kill(SIGTERM, $childPid)
似乎无能为力。
我怎样才能真正杀死孩子?
答案 0 :(得分:6)
应该是这样的。这不符合最佳做法,但它可以帮助您解决问题......
#!/usr/bin/perl
use warnings;
use strict;
use POSIX qw(:sys_wait_h);
my $timeOut = 5;
$SIG{ALRM} = \&timeout;
$SIG{CHLD} = 'IGNORE',
alarm($timeOut);
my $childPid = fork();
if ($childPid) {
while(1) {
print "[$$]: parent...\n";
sleep(2);
}
}else {
# I am the child - do something that blocks forever
while(1){
print "[$$]: child...\n";
sleep(2);
}
exit;
}
sub timeout {
print "killing $childPid\n";
print "###\n" . `ps -ef | grep -v grep | grep perl` . "###\n";
if ( ! (waitpid($childPid, WNOHANG)) ) {
print "killing $childPid...\n";
kill 9, $childPid;
die "[$$]: exiting\n";
}
}
输出:
$ forktest.pl
[24118]: parent...
[24119]: child...
[24118]: parent...
[24119]: child...
[24118]: parent...
[24119]: child...
killing 24119
###
cblack 24118 12548 0 14:12 pts/8 00:00:00 /usr/bin/perl ./forktest.pl
cblack 24119 24118 0 14:12 pts/8 00:00:00 /usr/bin/perl ./forktest.pl
###
killing 24119...
[24118]: exiting
答案 1 :(得分:4)
来自perldoc fork:
如果你在没有等待孩子的情况下分叉,你就会积累 僵尸。在某些系统上,您可以通过将$ SIG {CHLD}设置为来避免这种情况 “IGNORE”。
将$SIG{CHLD} = 'IGNORE';
添加到代码顶部时,我能够获得所需的行为。
[ben@imac ~]$ perl test.pl
numKilled: 1
[ben@imac ~]$
或者,在waitpid($childPid, 0);
之后添加kill
也可以。
答案 2 :(得分:1)
您的脚本中use
和strict
都POSIX
,因此SIGTERM
被解释为赤字"SIGTERM"
,这不是&{ #39; t以有用的方式行事。
use strict
将此意外裸字变为错误,然后use POSIX
提取SIGTERM
常量。
答案 3 :(得分:1)
我也看到了这种行为,即使在进程消失之后,kill 0也会返回true;我怀疑你可能会错过对waitpid的调用(通常在SIGCHLD处理程序中完成),这会导致这种情况,但即使添加它之后,kill 0仍然会返回true。
我建议您使用非阻塞waitpid而不是kill(并验证该进程实际上是否死于SIGTERM信号 - 有些可能不会,至少立即)。如果SIGTERM不起作用,您可能还想在一段时间后尝试SIGINT,并在最后的手段SIGKILL。
答案 4 :(得分:1)
尝试终止进程组(使用 - $ pid):
kill TERM => -$pid;
见perlipc。
答案 5 :(得分:0)
我认为问题在于' 0'你正在作为杀死'的第一个参数传递。当我阅读文档时,他们会说' 0' 0只是检查是否可以发送信号到过程,而不发送它。在您的情况下,您想发送' KILL'向子进程发出信号,所以这样做:
kill( 'KILL', $childPid );