当父母被杀时杀死孩子

时间:2016-07-07 01:49:12

标签: perl fork

当父母被杀时,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 );
}

3 个答案:

答案 0 :(得分:6)

注意使用END块的第二个示例更完整 注意最后,有关如何使用进程组的示例。

大部分时间都是您处理SIGTERM信号的机会。为此,您可以通过处理程序安排清理子进程。有些信号无法被捕获,特别是SIGKILLSIGSTOP。对于那些你必须转到操作系统的人,每个答案都是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 perlipcwaitpid。我们还检查它们是否全部消失(并收获)。

由于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,请参阅系统上的TERMman kill代表进程组,由减号表示。该数字与进程ID相同,但将-pid添加到代码中以查看。如果shell没有简单地启动这个过程,而是从另一个脚本说,它将属于其父进程组,其子进程也是如此。然后,您需要首先设置自己的进程组,其子进程将继承,然后您可以say getpgrp; 那个进程组。请参阅setpgrpgetpgrp,然后搜索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:不需要宏。)