Perl 杀死父对象时杀死子对象

Perl 杀死父对象时杀死子对象,perl,fork,Perl,Fork,当父进程被终止时,Perl是否有一种简单的方法终止子进程?当我在父PID上运行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 ) {

当父进程被终止时,Perl是否有一种简单的方法终止子进程?当我在父PID上运行
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 );
}

这是必须在操作系统上完成的事情,以确保可靠性。Linux有一个
prctl
(进程控制)系统调用,它多路复用许多函数,类似于
ioctl
。其中一个函数(操作码
PR\u SET\u PDEATHSIG
)安排进程在其父进程死亡时接收特定信号(作为第二个参数传递)

有一个换行
prctl
,它作为一个函数显示,该函数接受信号号

如果没有
prctl
,子进程(它不是由init进程生成的,因此最初具有除1之外的父进程ID)可以定期测试
getppid()的值。如果该值变为1,则表示其原始父代已死亡


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

注意第二个例子,使用
END
块,更完整。
注意最后讨论了如何使用流程组


大多数情况下,您可能正在处理
SIGTERM
信号。为此,您可以通过处理程序安排清理子进程。存在无法捕获的信号,尤其是
SIGKILL
SIGSTOP
。对于那些你将不得不去操作系统,每个答案由。这是给其他人的素描。另请参见下面的其他代码、下面的注释以及最后的流程组使用

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&
的形式运行它时,它会打印出来

[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 这里我们在
SIGCHLD
处理程序中获取(所有)终止的子进程,请参阅和。最后我们还要检查它们是否都消失了(并且收获了)

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

一些注释。这是一个需要考虑的事情的清单。除了上面提到的(和其他)Perl文档之外,还可以看到UNIX和C文档,因为Perl的ipc是直接在UNIX系统工具上构建的

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

  • 等待特定进程是阻塞的,因此如果某些进程没有终止,程序将挂起。非阻塞
    waitpid
    还有另一个警告,请参见链接的
    perlipc
    docs

  • 子进程可能在父进程被终止之前已退出。
    kill'TERM'
    发送
    SIGTERM
    ,但这并不确保子项终止。过程可能存在,也可能不存在

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

  • 有一些模块可用于这方面的各个方面。例如,参见pragma

  • 最好不要在信号处理程序中做太多工作

  • 发生的事情很多,错误可能会产生意想不到的深远后果


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

prompt> kill -s TERM -pid prompt>kill-s TERM-pid
您可能必须使用数字信号,通常为
15
对于
术语
,请参阅系统上的
杀人
-pid
代表流程组,由减号表示。该编号与进程ID相同,但添加
say getpgrp要查看的代码。如果这个进程不是简单地由shell启动的,而是从另一个脚本启动的,那么它将属于其父进程组,其子进程组也将属于其父进程组。然后您需要首先设置它自己的进程组,它的子进程将继承该进程组,然后您可以
kill
该进程组。请参见和。

以下内容如何:

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

(9是SIGKILL:不需要宏。)

注意
exec(“sleep”,$sleeptime)或die将比
系统(“sleep$sleeptime”)简单得多;退出。这会将示例中的进程数从10个减少到4个。听起来您想杀死一个进程组,而不仅仅是一个进程组process@ikegami,我数了7个进程,而不是10个,因为每个fork()+system()加上两个进程(),但这一点很好。@pilcrow,哦,是的,忘记了您提到的优化。请始终整理和缩进您的代码,并在您请求帮助的Perl程序上使用strict
和使用warnings'all
。您正在请求免费帮助,我认为您至少可以让代码可读。这次我是为你做的;请在将来再想想,如果父级在到达该行代码之前死亡,那么调用该调用的是什么?@Kaz一个浮动连字符很容易被忽略。我赞成
0-getpgid
kill( -1 * getpgid(), 9 );