Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
619 views
in Technique[技术] by (71.8m points)

perl - Propagation of signal to parent when using system

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";
    }
}

and child.pl:

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

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

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

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

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?

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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

system($shell_command)

is short for

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

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

system($shell_command)

is short for

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

As such,

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")

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


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

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.

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 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:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...