【问题标题】:Kill children when the parent is killed [duplicate]当父母被杀时杀死孩子[重复]
【发布时间】:2016-11-09 05:47:26
【问题描述】:

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

【问题讨论】:

  • 请注意,exec("sleep", $sleeptime) or die; 将比 system("sleep $sleeptime"); exit; 简单得多。它会将您示例中的进程数从 10 个减少到 4 个。
  • 听起来你想杀死一个进程组,而不仅仅是一个进程
  • @ikegami,我数了 7 个进程,而不是 10 个,因为每个 fork() + system() 增加了两个进程 (system() bypassing the shell in this case),但这一点很好。
  • @pilcrow,哦,对了,忘了你提到的优化。
  • 在您寻求帮助的 Perl 程序上,请始终整理和缩进您的代码以及 use strictuse warnings 'all'。您正在寻求免费帮助,我认为您至少可以做的是让您的代码可读。这次我替你做了;以后请三思

标签: perl fork


【解决方案1】:

注意 第二个例子,使用END块,更完整。
注意 讨论如何使用进程组 因为这是最后。


大多数情况下,您正在处理SIGTERM 信号。为此,您可以安排通过处理程序清理子进程。有些信号不能被捕获,特别是SIGKILLSIGSTOP。对于那些你必须去操作系统的人,根据Kaz 的回答。这是其他人的草图。另请参阅下面的其他代码,下面的 cmets,以及最后使用的 进程组

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 父 pid:4974 已启动流程:4978 4979 4980 4982 提示>杀死4974 有期限。清理子进程。 将 TERM 发送到进程 4978 4979 4980 4982 发出信号的进程数:4 在signals.pl第25行为TERM信号消亡。 在 END 块中... [13] 出口 4 信号.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 }

在这里,我们在SIGCHLD 处理程序中获取(所有)已终止的子进程,请参阅Signals in perlipcwaitpid。最后,我们还会检查它们是否都消失了(并收获了)。

kill 0, $pid 即使孩子是僵尸(已退出但未收割)也会返回 true,这可能会在测试中发生,因为父母会在之后检查 right。如果需要,在clean_up_kids() 之后添加sleep 1

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

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

  • 等待特定进程是阻塞的,因此如果某些进程没有被终止,程序将挂起。非阻塞 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

【讨论】:

    【解决方案2】:

    这是必须在操作系统中完成的事情才能可靠。 Linux 有一个prctl(过程控制)系统调用,它复用了许多功能,类似于ioctl。其中一个函数(操作码PR_SET_PDEATHSIG)安排进程在其父进程死亡时接收特定信号(作为第二个参数传递)。

    有一个 CPAN module 包裹了 prctl,它显示为一个接受信号编号的 set_pdeathsig 函数。

    如果没有prctl,子进程(它不是由 init 进程产生的,因此最初的父进程 ID 不是 1)可以定期测试 getppid() 的值。如果更改为 1,则意味着其原始父级已死亡。

    管理子进程的进程也可以实现优雅的关闭逻辑:捕获它们的终止信号并终止它们的子进程作为清理的一部分。当然,对于无法处理的信号,这是不可能的。

    【讨论】:

      【解决方案3】:

      以下内容如何:

      kill( -1 * getpgid(), 9 );
      

      (9 是 SIGKILL:不需要宏。)

      【讨论】:

      • 如果父级在到达那行代码之前就死了,会调用这个调用什么?
      • @Kaz 浮动连字符很容易被忽略。我赞成0 - getpgid
      猜你喜欢
      • 2012-01-03
      • 2014-09-16
      • 1970-01-01
      • 2020-08-07
      • 2010-12-02
      • 1970-01-01
      • 2011-09-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多