当父母被杀时杀死孩子

Cra*_*ide 2 perl fork

当父母被杀时,Perl是否有一种简单的方法可以杀死子进程?当我kill在父PID上运行时,孩子们仍然活着.

测试脚本:

#!/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 );
}
Run Code Online (Sandbox Code Playgroud)

zdi*_*dim 7

注意   使用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
}
Run Code Online (Sandbox Code Playgroud)

当我运行它signals.pl &然后杀死它时,它会打印出来

[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.

通过礼貌dieEND块被执行有序的,所以你可以清理子进程存在.这样你也可以防止未被捕获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 }
Run Code Online (Sandbox Code Playgroud)

这里我们在SIGCHLD处理程序中收集(全部)终止子进程,请参阅perlipcwaitpid中的信号.我们还检查它们是否全部消失(并收获).

由于kill 0, $pid返回true即使孩子是僵尸(退出但没有收获),这可能因为父检查测试发生后.添加sleep 1后,clean_up_kids()如果需要的话.

一些笔记.这远不是要考虑的完整列表.除了提到的(和其他)Perl文档之外,还可以看到UNIX和C文档,因为Perl的ipc是直接在UNIX系统工具上构建的.

  • 实际上,这里省略了所有错误检查.请加.

  • 等待特定进程是阻塞的,所以如果一些没有被终止,程序将挂起.非阻塞waitpid有另一个警告,请参阅链接的perlipc文档.

  • 在父母被杀之前,子进程可能已经退出.该kill 'TERM'发送SIGTERM但这并不能保证孩子终止.流程可能存在也可能不存在.

  • 信号处理程序可能会在END阶段失效,请参阅此文章.在我的测试中,CHLD仍然在这里处理,但如果这是一个问题,重新安装处理程序,如链接的答案.

  • 有各种方面的模块.例如,请参阅sigtrap pragma.

  • 建议不要在信号处理程序中做很多事情.

  • 有很多事情发生,错误会产生意想不到的后果.


如果你kill进程组,你将不会遇到任何这些问题,因为所有的孩子也会被终止.在我的系统上,这可以在终端上完成

prompt> kill -s TERM -pid

您可能必须使用数字信号,通常15用于TERM查看man kill您的系统.该-pid代表的进程组,由减号所指.该数字与进程ID相同,但添加say getpgrp;到要查看的代码中.如果shell没有简单地启动这个过程,而是从另一个脚本说,它将属于其父进程组,其子进程也是如此.然后,您需要首先设置自己的进程组,其子进程将继承,然后您可以进入kill 进程组.请参阅setpgrpgetpgrp,并搜索SO帖子.