This question already has answers here:
In perl, killing child and its children when child was created using open

(4个答案)


4年前关闭。




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

最佳答案

注意。第二个示例使用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

The processes do get killed, checked by ps aux | egrep '[s]leep' before and after kill.

By courtesy of die the END block gets executed orderly so you can clean up child processes there. That way you are also protected against uncaught die. So you'd use the handler merely to ensure that the END block cleanup happens.

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的ipc是直接在UNIX系统工具上构建的,因此与提到的Perl文档一起(以及其他),Perl文档也可以看到UNIX和C文档。
  • 实际上,这里省略了所有错误检查。请添加
  • 等待特定进程被阻止,因此如果某些进程没有终止,则程序将挂起。非阻塞waitpid有另一个警告,请参阅链接的perlipc文档
  • 在父进程被杀死之前,子进程可能已经退出。 kill 'TERM'发送SIGTERM,但这不能确保子项终止。进程可能存在或可能不存在
  • 信号处理程序可能会在END阶段失效,请参阅this post。在我的测试中,仍然在此处处理CHLD,但是如果这有问题,请重新安装处理程序,如链接的答案
  • 有用于此方面各个方面的模块。参见sigtrap pragma,例如
  • 建议不要在信号处理程序中做太多事情
  • 发生了很多事情,错误可能会带来意想不到且范围广泛的后果


  • 如果您用kill进程组,那么您将不会遇到任何这些问题,因为所有子进程也会被终止。在我的系统上,这可以在终端上通过

    提示> kill -s TERM -pid

    您可能必须使用数字信号,通常将15用作TERM,请参见系统上的man kill-pid代表过程组,由减号表示。该数字与进程ID相同,但是将say getpgrp;添加到要查看的代码中。如果不是仅由 shell 程序启动该进程,而是从另一个脚本启动,则它将属于其父进程组,其子进程也将属于该进程组。然后,您需要首先设置自己的进程组,其子进程将继承该进程组,然后可以对该进程组进行kill。参见setpgrpgetpgrp

    关于perl - parent 被杀时杀死 child ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38236478/

    10-15 10:38