当发送到包含子级的 perl 脚本时,SIGINT (^C) 会发生什么? [英] What happens to a SIGINT (^C) when sent to a perl script containing children?

查看:19
本文介绍了当发送到包含子级的 perl 脚本时,SIGINT (^C) 会发生什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个分叉的 Perl 脚本.

I have a Perl script that forks.

每个 fork 运行一个外部程序,解析输出,并将输出转换为可存储文件.

Each fork runs an external program, parses the output, and converts the output to a Storable file.

然后父级读取可存储文件,并分析每个子级的总数据,然后继续前一个分支的重复,否则父级将停止.

The Storable files are then read in by the parent and the total data from each of the children are analyzed before proceeding onto a repeat of the previous fork or else the parent stops.

当我发出 ^C 而一些孩子仍在运行外部程序时究竟会发生什么?父 perl 脚本在前台调用,并且我认为尽管进行了分叉,但它仍保留在前台.

What exactly happens when I issue a ^C while some of the children are still running the external program? The parent perl script was called in the foreground and, I presume, remained in the foreground despite the forking.

SIGINT 是否传递给所有子进程,即父进程、父进程的子进程、子进程调用的外部程序??

Is the SIGINT passed to all children, that is, the parent, the parent's children, and the external program called by the children??

更新:

我应该补充一点,当我发出 SIGINIT 时,我的脚本的子进程调用的外部程序似乎确认了信号并终止.但是孩子们,或者可能是家长计划,继续进行.这一切我都不清楚.

I should add, it appears that when I issue the SIGINIT, the external program called by the children of my script seem to acknowledge the signal and terminate. But the children, or perhaps the parent program, carry on. This is all unclear to me.

更新 2:

关于tchrist的注释,外部程序是用Perl的system()命令调用的.

With respect to tchrist's comment, the external program is called with Perl's system() command.

事实上,tchrist 的评论似乎也包含了我正在寻找的解释.经过更多的调试,根据我的程序的行为,似乎确实,SIGINT 正在从父级传递给所有子级,并从所有子级传递给它们的所有子级(外部程序).

In fact, tchrist's comment also seems to contain the explanation I was looking for. After some more debugging, based on the behavior of my program, it appears that, indeed, SIGINT is being passed from the parent to all children and from all children to all of their children (the external program).

因此,根据 tchrist 的评论,似乎正在发生的事情是 CTRL-C 正在杀死导致孩子移出 system() 命令的外部程序 - 仅此而已.

Thus, what appears to be happening, based on tchrist's comment, is that CTRL-C is killing the external program which causes the children to move out of the system() command - and nothing more.

尽管我让我的孩子检查了 system() 中调用的退出状态,但我假设 CTRL-C 会杀死从父级向下的所有内容,而不是导致创建更多轮的处理,这就是发生的事情!!!

Although I had my children check the exit status of what was called in system(), I was assuming that a CTRL-C would kill everything from the parent down, rather than lead to the creation of more rounds of processing, which is what was happening!!!

解决方案(针对我的问题):

我只需要在父级中为 SIGINT 创建一个信号处理程序.然后信号处理程序会向每个孩子发送 SIGTERM(我认为这也会向孩子的孩子发送一个 SIGTERM),然后让父母优雅地退出.虽然这个有点明显的解决方案可能会修复一些问题,但我想了解我对 SIGINT 在 Perl 中分叉的行为的误解.

I need to just create a signal handler for SIGINT in the parent. The signal handler would then send SIGTERM to each of the children (which I presume would also send a SIGTERM to the children's children), and then cause the parent to exit gracefully. Although this somewhat obvious solution likely would have fixed things, I wanted to understand my misconception about the behavior of SIGINT with respect to forking in Perl.

推荐答案

Perl 的内置 system 函数就像标准 C 库中的 C system(3) 函数一样工作就信号而言.如果您使用的是 Perl 版本的 system() 或管道打开或反引号,那么父级 - 调用 system 的那个而不是它调用的那个 - 将忽略任何 SIGINT和 SIGQUIT 在孩子们跑步时.如果您已经使用 fork?wait:exec 三重奏的某些变体推出了自己的产品,那么您必须自己考虑这些问题.

Perl’s builtin system function works just like the C system(3) function from the standard C library as far as signals are concerned. If you are using Perl’s version of system() or pipe open or backticks, then the parent — the one calling system rather than the one called by it — will IGNORE any SIGINT and SIGQUIT while the children are running. If you’ve you rolled your own using some variant of the fork?wait:exec trio, then you must think about these matters yourself.

考虑当你使用 system("vi somefile") 并在 vi 中长时间搜索时点击 ^C 会发生什么:只有 vi需要一个(非致命的)信号;父母忽略它.这是正确的行为.这就是 C 以这种方式工作的原因,也是 Perl 以这种方式工作的原因.

Consider what happens when you use system("vi somefile") and hit ^C during a long search in vi: only vi takes a (nonfatal) SIGINT; the parent ignores it. This is correct behavior. That’s why C works this way, and that’s why Perl works this way.

您必须记住的是,仅仅因为 ^C 向前台进程组中的所有进程(即使是那些具有不同有效 UID 或 GID 的进程)发送一个 SIGINT,这并不意味着它会导致所有这些进程退出.^C 只是一个 SIGINT,旨在中断一个进程,而不是一个 SIGKILL,意味着在不问任何问题的情况下终止.

The thing you have to remember is that just because a ^C sends a SIGINT to all processes in the foregrounded process group (even those of differing effective UID or GID), that does not mean that it causes all those processes to exit. A ^C is only a SIGINT, meant to interrupt a process, not a SIGKILL, meant to terminate with no questions asked.

有很多种程序,在没有警告的情况下直接终止是错误的;编辑器就是这样的一个例子.邮件程序可能是另一个.对此要格外小心.

There are many sorts of program that it would be wrong to just kill off with no warning; an editor is just one such example. A mailer might be another. Be exceedingly careful about this.

许多程序选择性地忽略、捕获或阻止(意味着延迟传递)各种信号.只有 SIGINT 的默认行为是导致进程退出.您可以使用传统操作系统上的此类代码找出这是否发生,以及实际上是哪个信号导致它发生(除其他外):

Many sorts of programs selectively ignore, trap, or block (means delay delivery of) various sorts of signals. Only the default behavior of SIGINT is to cause the process to exit. You can find out whether this happened, and in fact which signal caused it to happen (amongst other things), with this sort of code on traditional operating systems:

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

请注意,例如,^C'd vinot 具有等待状态字,表明它因未捕获的 SIGINT 而死亡,因为没有一:它抓住了它.

Note carefully that a ^C’d vi, for example, will not have a wait status word indicating it died from an untrapped SIGINT, since there wasn’t one: it caught it.

有时您的孩子会在您的背后生养自己的孩子.凌乱但真实.因此,我有时会以这种方式对所有已知和未知的后代进行种族灭绝:

Sometimes your kids will go and have kids of their own behind your back. Messy but true. I have therefore been known, on occasion, to genocide all progeny known and unknown this way:

# scope to temporize (save+restore) any previous value of $SIG{HUP}
{
    local $SIG{HUP} = "IGNORE";
    kill HUP => -$$;   # the killpg(getpid(), SIGHUP) syscall
}

这当然不适用于 SIGKILL 或 SIGSTOP,它们不能像那样被忽略.

That of course doesn’t work with SIGKILL or SIGSTOP, which are not amenable to being IGNOREd like that.

您可能需要注意的另一件事是,在 5.8 版本之前,Perl 中的信号处理在历史上并不是可靠的安全操作.现在,但这是一个版本相关的问题.如果您还没有这样做,那么您绝对应该阅读 perlipc 联机帮助页 中的延迟信号,也许还有 PERL_SIGNALS perlrun 联机帮助页 中的变量.

Another matter you might want to be careful of is that before the 5.8 release, signal handling in Perl has not historically been a reliably safe operation. It is now, but this is a version-dependent issue. If you haven’t yet done so, then you should definitely read up on deferred signals in the perlipc manpage, and perhaps also on the PERL_SIGNALS envariable in the perlrun manpage.

这篇关于当发送到包含子级的 perl 脚本时,SIGINT (^C) 会发生什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆