【问题标题】:Send TERM signal to child process spawned in another thread of parent process向在父进程的另一个线程中生成的子进程发送 TERM 信号
【发布时间】:2013-05-16 09:27:43
【问题描述】:

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

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

【问题讨论】:

    标签: linux multithreading perl signals multiprocessing


    【解决方案1】:

    你为什么说 SIGTERM 处理程序不起作用?因为孩子变成了僵尸?

    除非您等待它们,否则所有子进程都会变成僵尸。将waitpid($pid, 0); 放在kill() 之后。除非您在打印输出中看到 in child process: cycle 5,否则 kill 和信号处理程序工作正常。

    请注意,使用硬编码文件在分叉进程和主进程之间进行通信是非常不稳定的。我建议你使用管道。

    编辑:

    Wrt 你的 sig 处理程序没有被调用,我认为这是一个 perl 错误。 perl 只向主线程发送信号。当你 fork() 时,你的线程成为主线程,但我认为 perl 没有意识到这一点。不过,您可以通过将信号重新转发给自己来解决此问题。

    在创建线程之前,只需添加:

    sub sigforwarder {
        threads->self()->kill(shift);
    }
    $SIG{TERM} = \&sigforwarder;
    

    应该解决你的问题

    【讨论】:

    • 谢谢,硬编码文件只是数据库的简化版本,所以这个程序是我的 web 应用程序的一部分的简单模型,它使用数据库来存储 pid。对于僵尸部分,对不起,这是我的错,我忘了添加收割者功能,但在我的网络应用程序中,我确实安装了 CHLD 信号的处理程序。我只是想知道为什么print "child $$ exits!\n"; 不起作用以及为什么孩子没有向父母发送 CHLD 信号。
    • 我通过在程序中添加收割者来编辑问题。但无论如何它都没有被调用。
    • 我不太明白...这不是无休止的递归吗? sigforwarder 捕获TERM 信号,并重新发送TERM 信号给孩子,然后再次调用sigforwarder
    • 是的,看起来像。正如我上面所说,我不太确定您所看到的不是 perl 中的错误。 perl 似乎没有意识到子进程中只有一个线程,并且表现得好像有 2 个。sigforwarder 只允许“子线程”获取信号。 perl 将仅在收到信号时运行进程信号处理程序,并且在您转发之后运行线程 sig 处理程序,因此没有递归。请注意,您可以通过在主线程而不是子线程中分叉来简单地解决问题
    • 好的,谢谢,我会尝试为我的程序找到解决方法,而不是在子线程上分叉。
    【解决方案2】:

    我认为问题在于 child_exit 不会在父线程上执行任何退出。它只是退出了孩子。我不确切知道 perl 中发生了什么(只是偶然发现了 c 中的 fork/exec 构造),但我会尝试在父进程中捕获 SIG_CHILD 以检测其终止。

    (“child $$ exits”这个消息是在child的1中输出的,所以不会出现在屏幕上吧?)

    编辑:我刚刚尝试了您的示例,我想我明白了:您正在子进程中执行分叉并检查 pid 是否为 0(这是父异步进程)。您可以只检查 != 0。

    【讨论】:

    • 孩子的1其实是STDOUT,一般会打印出来。我觉得不同的平台不同的线程模型可能会有不同的行为,很好奇里面发生了什么……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多