EMi*_*ler 14 perl fork parent-child sigint
我有一个分叉的Perl脚本.
每个fork运行一个外部程序,解析输出,并将输出转换为Storable文件.
然后由父母读入可存储文件,并且在继续重复前一个分支之前分析来自每个孩子的总数据,否则父母停止.
当一些孩子仍在运行外部程序时发出^ C时到底发生了什么?在前台调用父perl脚本,我认为,尽管有分叉,仍保留在前台.
是否将SIGINT传递给所有子节点,即父节点,父节点的子节点以及子节点调用的外部程序?
更新:
我应该补充一点,看来当我发出SIGINIT时,我脚本的子节点调用的外部程序似乎会确认信号并终止.但孩子们,或者父母的计划,继续下去.这一点对我来说都不清楚.
更新2:
关于tchrist的评论,外部程序是用Perl的system()命令调用的.
事实上,tchrist的评论似乎也包含了我正在寻找的解释.经过一些调试后,根据我的程序的行为,看来确实,SIGINT正从父级传递给所有子级,并且从所有子级传递给所有子级(外部程序).
因此,根据tchrist的评论,似乎正在发生的事情是CTRL-C正在杀死外部程序,导致孩子们离开system()命令 - 仅此而已.
虽然我让我的孩子检查了所调用内容的退出状态system(),但我假设CTRL-C会从父节点中删除所有内容,而不是导致创建更多轮处理,这就是发生的事情! !
解决方案(对我的问题):
我需要在父级中为SIGINT创建一个信号处理程序.然后信号处理程序将SIGTERM发送给每个子节点(我假设它也会向子节点的子节点发送SIGTERM),然后使父节点正常退出.虽然这个有点明显的解决方案可能会解决问题,但我想理解我对于在Perl中分叉的SIGINT行为的误解.
tch*_*ist 13
就信号而言,Perl的内置system函数就像标准C库中的C 系统(3)函数一样工作.如果您正在使用Perl的版本system()或管道打开或反引号,那么父项 - 调用它system而不是它调用的那个 - 将在子项运行时忽略任何SIGINT和SIGQUIT.如果你使用fork?wait:exec三重奏的某种变体来推动自己,那么你必须自己考虑这些问题.
考虑system("vi somefile")在长时间搜索中使用和点击^ C时会发生什么vi:只vi接受(非致命)SIGINT; 父母忽略了它.这是正确的行为.这就是为什么C以这种方式工作的原因,这就是Perl以这种方式工作的原因.
你要记住的事情是,仅仅因为一个^ C发送SIGINT的前景化进程组中的所有进程(甚至包括那些不同的有效UID或GID的),但这并不意味着它会导致所有这些进程退出.A ^ C只是一个SIGINT,意味着中断一个进程,而不是一个SIGKILL,意味着在没有问题的情况下终止.
有很多种程序可以在没有警告的情况下杀掉它; 编辑只是一个这样的例子.邮件可能是另一个邮件.对此非常小心.
许多类型的程序选择性地忽略,捕获或阻止(意味着延迟传送)各种信号.只有SIGINT的默认行为才能导致进程退出.您可以通过传统操作系统上的这类代码了解这是否发生了,实际上是哪个信号导致它发生(以及其他事情):
if ($wait_status = system("whatever")) {
$sig_killed = $wait_status & 127;
$did_coredump = $wait_status & 128;
$exit_status = $wait_status >> 8;
# now do something based on that...
}
Run Code Online (Sandbox Code Playgroud)
请注意vi,例如,^ C'd 将没有等待状态字,表明它死于未捕获的SIGINT,因为没有一个:它抓住了它.
有时你的孩子会去背后拥有自己的孩子.凌乱而真实.因此,我有时会以这种方式对所有已知和未知的后代进行种族灭绝:
# scope to temporize (save+restore) any previous value of $SIG{HUP}
{
local $SIG{HUP} = "IGNORE";
kill HUP => -$$; # the killpg(getpid(), SIGHUP) syscall
}
Run Code Online (Sandbox Code Playgroud)
那当然不适用于SIGKILL或SIGSTOP,它们不适合像这样的IGNOREd.
您可能需要注意的另一个问题是,在5.8版本发布之前,Perl中的信号处理在历史上并不是一个可靠的安全操作.它是现在,但是这是一个不同版本的不同问题.如果您还没有这样做,那么您应该在perlipc联机帮助页中阅读 延迟信号,也可以在perlrun联机帮助页PERL_SIGNALS中的envariable上阅读.
当您在终端窗口(或任何终端)中点击^ C时,它会将SIGINT发送到该终端中的前台进程GROUP.现在,当您从命令行启动程序时,它通常位于其自己的进程组中,并成为前台进程组.默认情况下,当您分叉子项时,它将与父项在同一个进程组中,因此默认情况下,父项(从命令行调用的顶级程序),所有子项,子项的子项等,以及任何这些(所有这些都只是孩子)调用的外部程序都将在同一个进程组中,因此都会收到SIGINT信号.
但是,如果这些子项或程序中的任何一个调用setpgrp或setpgid或setsid,或任何其他调用导致该进程成为新进度组中的那些进程(那些进程(以及它们在离开前台进程组后启动的任何子进程)将不会收到SIGINT.
此外,当一个进程收到一个SIGINT时,它可能不会终止 - 它可能会忽略该信号,或者它可能会捕获它并做一些完全不同的事情.SIGINT的默认信号处理程序终止进程,但这只是可以覆盖的默认值.
编辑
从您的更新中,听起来好像所有内容都保留在同一个进程组中,因此当孙子(外部程序)退出时,信号正在传递给每个人,但是孩子们正在捕捉并忽略了SIGINT.通过tchrist的评论,听起来这是perl的默认行为.