Reputation: 351
Following code behaves as expected when running from terminal:
perl -e 'kill -2, $$; warn HERE, $/'
It sends itself SIGINT
and dies before reaching "HERE":
~# perl -e 'kill -2, $$; warn HERE, $/'
~# echo $?
130
~#
The problem: same code fails to kill self PID when running from shell script:
~# cat 1.sh
perl -e 'kill -2, $$; warn HERE, $/'
~#
~# sh 1.sh
HERE
~#
~# echo $?
0
~#
On the other hand, replacing perl's kill
by a shell's one works OK:
~# cat 2.sh
perl -e 'qx/kill -2 $$/; warn HERE, $/'
~#
~# sh 2.sh
~#
~# echo $?
130
~#
Not really understand what is happening here, please help..
Upvotes: 4
Views: 723
Reputation: 385789
First of all,
kill -2, $$
is better written as
kill 2, -$$
An even better alternative is
kill INT => -$$
These send SIGINT
to the specified process group.
Your main question appears to be why the two shells behave differently. This section explains that.
The process group represents an application.
When you launch a program from an interactive shell, it's not part of a larger application, so the shell creates a new process group for the program.
However, processes created by a script (i.e. a non-interactive shell) are part of the same application as the script itself, so the shell doesn't create a new process group for them.
You can visualize this using the following:
sh -i <<< 'perl -e '\''system ps => -o => "pid,ppid,pgrp,comm"'\'''
outputs the following:
$ perl -e 'system ps => -o => "pid,ppid,pgrp,comm"'
PID PPID PGRP COMMAND
8179 8171 8179 bash
14654 8179 14654 sh
14655 14654 14655 perl
14656 14655 14655 ps
$ exit
In interactive mode, perl
is at the head of perl
and ps
's program group.
sh <<< 'perl -e '\''system ps => -o => "pid,ppid,pgrp,comm"'\'''
outputs the following:
PID PPID PGRP COMMAND
8179 8171 8179 bash
14584 8179 14584 sh
14585 14584 14584 perl
14586 14585 14584 ps
In non-interactive mode, sh
is at the head of perl
and ps
's program group.
Your failures are the result of not sending the signal to the head of the process group (i.e. the application). Had you checked, the error kill
reported was ESRCH
("No such process").
ESRCH
The pid or process group does not exist. [...]
To kill the current process's process group, replace the improper
kill INT => -$$ # XXX
with
kill INT => -getpgrp() # Kill the application
You can make your perl
the head of its own process group by simply calling the following:
setpgrp();
Test:
$ sh <<< 'perl -e '\''system ps => ( -o => "pid,ppid,pgrp,comm" )'\'''
PID PPID PGRP COMMAND
8179 8171 8179 bash
16325 8179 16325 sh
16326 16325 16325 perl
16327 16326 16325 ps
$ sh <<< 'perl -e '\''setpgrp(); system ps => ( -o => "pid,ppid,pgrp,comm" )'\'''
PID PPID PGRP COMMAND
8179 8171 8179 bash
16349 8179 16349 sh
16350 16349 16350 perl
16351 16350 16350 ps
That's not something you normally want to do.
Finally, the Perl code
kill INT => -$pgrp
is equivalent to the following call of the kill
command-line utility:
kill -s INT -$pgrp
kill -INT -$pgrp
kill -2 -$pgrp
You were missing -
in your qx//
program, so it was sending SIGINT to the identified process rather than the identified program group.
Upvotes: 5
Reputation: 58544
From your interactive terminal, the perl process kills the process group of which it is a part. (The shell runs perl in its own process group.) The shell reports this unusual termination in $?
:
t0 interactive shell (pid=123, pgrp=123) | t1 +------> perl -e (pid=456, pgrp=456, parent=123) | | t2 (wait) kill(-2, 456) (in perl, same as kill pgrp 456 w/ SIGINT) | | t3 (wait) *SIGINT* | t4 report $?
From your shell script, the perl process kills a (likely) non-existent process group and then exits successfully. Your interactive shell makes a new process group in which to run your shell script, and that script then runs perl as a child in the same process group.
t0 shell (pid=123, pgrp=123) | t1 +-------> shell:1.sh (pid=456, pgrp=456, parent=123) | | t2 (wait) +-------------> perl -e (pid=789, pgrp=456, parent=456) | | | t3 (wait) (wait) kill pgrp 789 with SIGINT (error: no such pgrp) | | | t4 (wait) (wait) exit success | | t5 (wait) exit success | t6 report $?
In your backticked (qx//
) example, your interactive shell starts a shell process with a new process group. (Not that it matters here, but that process runs perl in its same process group.) Perl then runs as its own child the system kill
command, the semantics of which differ from that of the perl kill
. This grandchild command sends a SIGINT to the perl PID directly, rather than a SIGINT to a process group. Perl terminates, and that exit code is conveyed as the script's exit code, since it was the last command in the script.
This diagram is a little busier than the previous:
t0 shell (pid=123, pgrp=123) | t1 +-------> shell:2.sh (pid=456, pgrp=456, parent=123) | | t2 (wait) +----------> perl -e (pid=789, pgrp=456, parent=456) | | | t3 (wait) (wait) +---------> /bin/kill SIGINT 789 | | | | t4 (wait) (wait) *SIGINT* exit success | | t5 (wait) return $? | t6 report $?
Upvotes: 4
Reputation: 2863
It works fine in this way:
perl -E 'say "kill "INT", $$; warn HERE, $/'
perl -E 'say "kill 2, $$; warn HERE, $/'
kill man page says:
A negative signal name is the same as a negative signal number, killing process groups instead of processes. For example, kill '-KILL', $pgrp and kill -9, $pgrp will send SIGKILL to the entire process group specified. That means you usually want to use positive not negative signals.
Upvotes: 0