我在Linux平台上使用Perl。首先,我创建了一个线程,并在这个新线程中分叉了一个子进程。当新线程中的父节点返回并加入主线程时,我想将TERM信号发送到在创建的线程中生成的子进程,但是信号处理程序不起作用,并且子进程变为僵尸。这是我的代码:
use strict;
use warnings;
use Thread 'async';
use POSIX;
my $thrd = async {
my $pid = fork();
if ($pid == 0) {
$SIG{TERM} = \&child_exit;
`echo $$ > 1`;
for (1..5) {
print "in child process: cycle $_\n";
sleep 2;
}
exit(0);
}
else {
$SIG{CHLD} = \&reaper;
}
};
$thrd->detach();
sleep 4;
my $cpid = `cat 1`;
kill "TERM", $cpid;
while (1) {}
sub child_exit {
print "child $$ exits!\n";
exit(0);
}
sub reaper {
my $pid;
while (($pid = waitpid(-1, &WNOHANG)) > 0) {
print "reaping child process $pid\n";
}
}
有关如何在这种情况下成功安全地发送信号的任何建议吗?
答案 0 :(得分:2)
为什么你说SIGTERM处理程序不起作用?因为孩子变成了僵尸?
除非你等待,否则所有的孩子都会变成僵尸。将waitpid($pid, 0);
放在kill()
之后。除非你在打印输出中看到in child process: cycle 5
,否则kill和信号处理程序工作正常。
请注意,使用硬编码文件在分叉进程和主进程之间进行通信是非常不稳定的。我建议你使用烟斗。
编辑:
你的sig处理程序没有被调用,我认为这是一个perl bug。 perl仅向主线程发送信号。当你fork()时,你的线程成为主线程,但我认为perl没有意识到这一点。你可以通过将信号重新转发给自己来解决这个问题。
在创建线程之前,只需添加:
sub sigforwarder {
threads->self()->kill(shift);
}
$SIG{TERM} = \&sigforwarder;
应该解决您的问题
答案 1 :(得分:0)
我认为问题是,child_exit不会在父线程上执行任何退出。它只是退出了孩子。我不确切知道perl中发生了什么(只是偶然发现了c中的fork / exec-construction),但是我会尝试在父进程中捕获SIG_CHILD来检测它的终止。
(消息“child $$ exits”在孩子的1中输出,所以它在屏幕上不可见,对吗?)
编辑:我刚刚尝试了你的例子,我想我得到了它:你正在子进程中执行一个fork并检查pid是否为0(这是父async-Process)。你可能只是检查!= 0。