Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Re^2: using waitpid() with signals

by ristov (Initiate)
on Jan 21, 2017 at 08:40 UTC ( [id://1180070]=note: print w/replies, xml ) Need Help??


in reply to Re: using waitpid() with signals
in thread using waitpid() with signals

hi Ken,

the solution you have suggested looks like my last example, except that the presence of the child process is verified with kill(0, $pid), not waitpid($pid, WNOHANG). The use of kill() involves one caveat, though -- it merely checks that the process with the given PID exists, but that does not mean this process is a child. In order to illustrate this, suppose you are logged in as root and execute the following commandline:

perl -e 'kill(0, 1) && print "Init is our child\n"'

On my Linux laptop, this commandline always prints out the message "Init is our child", although this is not true. This caveat can lead to the following race condition -- if the signal handler gets triggered after waitpid($pid, 0) has returned *and* the OS has had enough time to start a new process with the same PID, the new process can mistakenly get the TERM signal. I acknowledge that modern operating systems attempt not to reuse the same PID immediately, but I wouldn't like to rely on that assumption in the code, especially because it will be executing with root privileges and can thus kill any process in the system.

In contrast, waitpid($pid, WNOHANG) returns 0 for running child processes only, and a new unrelated process with the same PID is not reported as a running child. That's why I was asking if it is OK to call waitpid() again from the signal handler, if the blocking waitpid() call was interrupted by the signal.

regards, risto

Replies are listed 'Best First'.
Re^3: using waitpid() with signals
by kcott (Archbishop) on Jan 21, 2017 at 21:40 UTC
    "This caveat can lead to the following race condition -- if the signal handler gets triggered after waitpid($pid, 0) has returned *and* the OS has had enough time to start a new process with the same PID, the new process can mistakenly get the TERM signal. I acknowledge that modern operating systems attempt not to reuse the same PID immediately, but I wouldn't like to rely on that assumption in the code, especially because it will be executing with root privileges and can thus kill any process in the system."

    You can put the local handler passing the TERM to the child, as well as the waitpid, in an anonymous block. When the waitpid returns, the anonymous block is exited, the handler is popped off the stack, and whatever previous handler was in effect is now in effect again.

    #!/usr/bin/env perl -l use strict; use warnings; use constant SLEEP_TIME => 20; my $pid = fork; die "Can't fork()" unless defined $pid; if ($pid) { local $SIG{TERM} = sub { print "Parent received signal: @_"; die "$$ committing suicide!\n"; }; { local $SIG{TERM} = sub { print "Parent received signal: @_"; print "$$ committing infanticide!"; kill TERM => $pid if kill 0 => $pid; }; print "Parent: $$; Child: $pid"; print "Waiting on child ..."; waitpid($pid, 0); } print "Child terminated."; print "$$ resting ..."; sleep SLEEP_TIME; print "$$ rested and exiting."; } else { print "Child: $$"; local $SIG{TERM} = sub { print "Child received signal: @_"; die "Child died via signal handler.\n"; }; sleep SLEEP_TIME; exit; }

    So I ran it first without any intervention; it slept as expected:

    Parent: 36789; Child: 36790 Waiting on child ... Child: 36790 Child terminated. 36789 resting ... 36789 rested and exiting.

    Next I started it running, then entered "kill -TERM 36797" twice in quick succession:

    Parent: 36797; Child: 36798 Waiting on child ... Child: 36798 Parent received signal: TERM 36797 committing infanticide! Child received signal: TERM Child died via signal handler. Child terminated. 36797 resting ... Parent received signal: TERM 36797 committing suicide!

    Finally, I entered "kill -TERM 36836" followed by "kill -TERM 36835":

    Parent: 36835; Child: 36836 Waiting on child ... Child: 36836 Child received signal: TERM Child died via signal handler. Child terminated. 36835 resting ... Parent received signal: TERM 36835 committing suicide!

    — Ken

      I see your point, thanks for the suggestion! But just out of curiosity -- is it safe to call non-blocking waitpid() from the signal handler, if the signal itself has interrupted a blocking waitpid()?

      kind regards, risto

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1180070]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (7)
As of 2024-04-23 16:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found