使用系统时将信号传播到父级 [英] Propagation of signal to parent when using system

查看:37
本文介绍了使用系统时将信号传播到父级的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一个包装脚本,它使用 system() 启动另一个脚本.子脚本捕获 SIGINT 并在内部处理异常.因此,它在退出时不应将 SIGINT 传播给其父级.但是,在某些情况下,父级仍会收到 SIGINT.例如 (parent.pl):

I have written a wrapper script that starts another script using system(). The child script traps SIGINT and processes the exception internally. Therefore, it should not propagate the SIGINT to its parent when it exits.. However, in some cases the parent still receives the SIGINT. For example (parent.pl):

use feature qw(say);
use strict;
use warnings;
use POSIX ();

my @data = (q(child.pl 'dummy'), q(child.pl), q(bash -c child.pl), q(sh -c child.pl));

for ( @data ) {
    say "Running command '$_'..";
    my $res = system $_;
    my $signal = $res & 127;
    my $rval = $res >> 8;
    say "Parent received return value: $rval";
    if ( $signal == POSIX::SIGINT ) {
        say "Parent received SIGINT";
    }
}

child.pl:

use feature qw(say);
use strict;
use warnings;

eval {
    local $SIG{INT} = sub { die "Aborted by user.\n" };
    sleep 10;
};
if ( $@ ) {
    print "\n" . $@;
    exit 0;
}
say "Timed out..";
exit 1;

如果我在超时前按 CTRL-C,输出看起来像:

If I press CTRL-C before the time out, the output looks like:

Running command 'child.pl 'dummy''..
^C
Aborted by user.
Parent received return value: 0
Parent received SIGINT
Running command 'child.pl'..
^C
Aborted by user.
Parent received return value: 0
Running command 'bash -c child.pl'..
^C
Aborted by user.
Parent received return value: 0
Running command 'sh -c child.pl'..
^C
Aborted by user.
Parent received return value: 0
Parent received SIGINT

因此,在第一种和最后一种情况下,父级会收到 SIGINT,而对于第二种和第三种情况,则不会.

So in the first and last case the parent receives the SIGINT, whereas for the second and third cases it does not.

这是什么原因?以及如何修复第一个和最后一个案例中不传播 SIGINT 的问题?

What is the reason for this? And how can it be fixed such that the SIGINT is not propagated for the first and last case?

(我怀疑这与 Shell 的类型有关,即 sh vs bash )

( I suspect it is related to the type of Shell, i.e. sh vs bash )

推荐答案

首先,让我们了解正在执行的内容.

First, let's be aware of what is being executed.

system($shell_command)

system({ "/bin/sh" } "/bin/sh", "-c", $shell_command)

除非 shell 命令不包含 shell 元字符,只有空格,在这种情况下

unless the shell command contains no shell meta-characters but whitespace, in which case

system($shell_command)

my @cmd = split(' ', $shell_command);
system({ $cmd[0] } @cmd)

因此,

system("child.pl 'dummy'")   is short for   system({ "/bin/sh" } "/bin/sh", "-c", "child.pl 'dummy'")
system("child.pl")           is short for   system({ "child.pl" } "child.pl")
system("bash -c child.pl")   is short for   system({ "bash" } "bash", "-c", "child.pl")
system("sh -c child.pl")     is short for   system({ "sh" } "sh", "-c", "child.pl")

值得注意的是,在这种特殊情况下,bashchild.pl 替换了自己,而不是在单独的进程中生成它.这使得 child.pl 在第三种情况下成为 parent.pl 的直接子级(就像第二种情况一样).

Of note is that bash replaces itself with child.pl instead of spawning it in a separate process in this particular circumstance. That makes child.pl a direct child of parent.pl in the third case (just like the second case).

其次,让我们了解 Ctrl-C 的作用.

Second, let's be aware of what Ctrl-C does.

当按下 Ctrl-C 时,终端将 SIGINT 发送到每个以该终端作为其控制终端的进程.换句话说,SIGINT 被发送到会话的每个进程.

When Ctrl-C is pressed, the terminal sends SIGINT to every process that has that terminal as its controlling terminal. In other words, SIGINT is sent to every process of the session.

如你所见,通过在 child.pl 中添加 system("ps -o pid,ppid,pgrp,sid,cmd");,有三个或我们会话中的四个进程取决于测试用例.

As you can see by adding system("ps -o pid,ppid,pgrp,sid,cmd"); to child.pl, there are three or four processes in our session depending on the test case.

  • child.pl:child.pl 处理 SIGINT.它没有被它杀死.
  • 在测试用例 1 和 4 中由 parent.pl 启动的外壳:外壳被 SIGINT 杀死.
  • parent.pl:system 相当于 local $SIG{INT} = 'IGNORE';,所以它忽略了 SIGINT.
  • 启动 parent.pl 的登录 shell:它忽略了 SIGINT,但我不知道为什么.我猜是因为它是一个交互式 shell.
  • child.pl: child.pl handles SIGINT. It isn't killed by it.
  • The shell started by parent.pl in test cases 1 and 4: The shell is killed by SIGINT.
  • parent.pl: system does the equivalent of local $SIG{INT} = 'IGNORE';, so it ignores SIGINT.
  • The login shell that started parent.pl: It ignores SIGINT, but I'm not sure why. I'm guessing it's because it's an interactive shell.

所以,这就是你所观察到的:

So, this is what you are observing:

  • parent.pl 的(直接)子节点是 child.pl [测试用例 2 和 3] 时,子节点 (child.plcode>) 不会死于 SIGINT,因为它处理 SIGINT.
  • parent.pl 的(直接)子进程是 shell [测试用例 1 和 4] 时,子进程(shell)因 SIGINT 而死亡,因为非交互式 shell 不处理/忽略信号情报.
  • When the (direct) child of parent.pl is child.pl [Test cases 2 and 3], the child (child.pl) does not die from SIGINT since it handles SIGINT.
  • When the (direct) child of parent.pl is a shell [Test cases 1 and 4], the child (the shell) dies from SIGINT since non-interactive shells don't handle/ignore SIGINT.

这篇关于使用系统时将信号传播到父级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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