将TERM信号发送到在父进程的另一个线程中生成的子进程

时间:2013-05-16 09:27:43

标签: linux multithreading perl signals multiprocessing

我在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";
    }
}

有关如何在这种情况下成功安全地发送信号的任何建议吗?

2 个答案:

答案 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。