tdlewis77 has asked for the wisdom of the Perl Monks concerning the following question:
I use exec to transfer control from one instance of my program to a fresh one (so, for example, code changes get applied). However, the signals don't seem to work right in the new process. $SIG{INT} is set to run my interrupt handler, but it doesn't. I see "^C" on the screen, but my code does not run. Is there something I need to do in the new process to ensure my signals work correctly. Or, is there a better way to reload my program?
Re: signals after exec
by soonix (Canon) on Aug 26, 2017 at 06:51 UTC
|
exec is a wrapper around the corresponding system call (actually, a whole family of functions). Their man pages, e.g. execve(2) and signal(7) say that "the dispositions of handled signals
are reset to the default; the dispositions of ignored signals are left
unchanged." | [reply] |
|
"the dispositions of handled signals are reset to the default; the dispositions of ignored signals are left unchanged."
... and if you think about it, it makes sense. Ignoring a signal just sets a flag somewhere in the process structure managed by the kernel. The same is true for the other default actions terminate, terminate and dump core, stop and continue. Custom signal handlers, i.e. every signal handler somewhere in the code of the process, are overwritten by exec() (Remember: exec() replaces the executable in the current process with a different one), and so the signal handler pointers would point to some nonsense. If exec() would not reset the signal handlers, invoking the first custom signal handler would crash the executable (by executing unrelated code).
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [d/l] [select] |
Re: signals after exec
by stevieb (Canon) on Aug 26, 2017 at 02:46 UTC
|
Please show what you've got now, as minimalistic as possible.
| [reply] |
|
./sigfail.pl
test> sig
INT: sigInt
test> loop
^CCaught
Reloading sigfail.pl
test> sig
INT: sigInt
test> loop
^C^C^C^C^C
use B;
use Term::ReadLine;
$SIG{INT} = \&sigInt;
$term = Term::ReadLine->new("sigfail");
while ($resp = $term->readline("test> "))
{
if ($resp =~ m/^exit/) { last; }
elsif ($resp =~ m/^loop/) { loop: goto loop; }
elsif ($resp =~ m/^reload/) { reload() }
elsif ($resp =~ m/sig/) { sig() }
else { print "What?\n"; }
}
sub codeToName
{
my ($coderef) = @_;
my $cv = B::svref_2object($coderef);
return "" unless defined($cv) && defined($cv->GV);
return $cv->GV->NAME;
}
sub reload { print "Reloading sigfail.pl\n"; exec("./sigfail.pl") }
sub sigInt { print "Caught\n"; reload() }
sub sig { map { print "$_: ",codeToName($SIG{$_}),"\n"
if defined($SIG{$_}) && ref($SIG{$_}) eq "CODE" }
sort { $a cmp $b } keys %SIG; }
| [reply] [d/l] |
|
I decided to go with using fork to invoke the reloadable code. In my final implementation the child will use require to load a bunch of code that the parent doesn't need so that when the child process terminates, that code gets unloaded. I wanted a simple way for the child to send a message on its deathbed to the parent. I considered various IPC options, but in the end decided it would be simplest to just write a message to a file. The sample code below includes verbose messaging to easily follow what's happening:
./sigsuccess.pl
test> sig
INT: sigInt
test> loop
^C
child 38021: INT
child 38021: Reloading sigsuccess.pl
parent 38020: ignore INT
parent 38020: CHLD (reload)
parent 38020: Reloading...
test> loop
^C
child 38023: INT
child 38023: Reloading sigsuccess.pl
parent 38020: ignore INT
parent 38020: CHLD (reload)
parent 38020: Reloading...
test> exit
child 38024: Child exiting
parent 38020: CHLD ()
parent 38020: exiting after waitpid
use B;
use Term::ReadLine;
$SIG{INT} = sub { print "parent $$: ignore INT\n" };
my $comm = "message.$$";
reload:
if ($pid = fork())
{
my $reload = 0;
local $SIG{CHLD} = sub
{
my $request = "";
if (open(my $fh,$comm))
{
$request = <$fh>;
chomp $request;
close $fh;
unlink $comm;
$reload = 1 if $request =~ m/^reload$/;
}
print "parent $$: CHLD ($request)\n";
};
waitpid($pid,0);
if ($reload)
{
print "parent $$: Reloading...\n";
goto reload;
}
print "parent $$: exiting after waitpid\n";
} else {
$SIG{INT} = \&sigInt;
$term = Term::ReadLine->new("sigsuccess");
while ($resp = $term->readline("test> "))
{
if ($resp =~ m/^exit/) { last; }
elsif ($resp =~ m/^loop/) { loop: goto loop; }
elsif ($resp =~ m/^reload/) { reload(); last; }
elsif ($resp =~ m/sig/) { sig() }
else { print "What?\n"; }
}
print "child $$: Child exiting\n";
exit;
}
sub codeToName
{
my ($coderef) = @_;
my $cv = B::svref_2object($coderef);
return "" unless defined($cv) && defined($cv->GV);
return $cv->GV->NAME;
}
sub reload
{
print "child $$: Reloading sigsuccess.pl\n";
open my $fh, ">$comm";
print $fh "reload\n";
close $fh;
exit(0);
}
sub sigInt { print "\nchild $$: INT\n"; reload() }
sub sig
{
map
{
print "$_: ",codeToName($SIG{$_}),"\n"
if defined($SIG{$_}) && ref($SIG{$_}) eq "CODE"
} sort { $a cmp $b } keys %SIG;
}
| [reply] [d/l] |
|
For what it's worth, this fails the same way on all of the following platforms:
- linux 3.10.0-229.14.1.el7.x86_64
- darwin 15.6.0
- cygwin 2.0.4(0.28753)
Except that CygWin doesn't print the ^C.
| [reply] |
Re: signals after exec
by zakame (Pilgrim) on Aug 26, 2017 at 16:48 UTC
|
Is there something I need to do in the new process to ensure my signals work correctly. Or, is there a better way to reload my program?
I think this sounds like you need a process manager to handle reloads of your program. Maybe something like Proc::Daemon could help?
| [reply] |
Re: signals after exec
by Anonymous Monk on Aug 28, 2017 at 02:23 UTC
|
| [reply] |
|
|