2
votes

I'm on Linux platform and using Perl. First of all I created a thread, and forked a child process in this new thread. When the parent in the new thread returned and joined to the main thread, I would like to send TERM signal to the child process spawned in the created thread, but the signal handler doesn't work, and the child process becomes zombie. Here's my code:

use strict;
use warnings;
use Thread 'async';
use POSIX;

my $thrd = async {
    my $pid = fork();
    if ($pid == 0) {
        $SIG{TERM} = \&child_exit;
        `echo $$ > 1`;
        for (1..5) {
            print "in child process: cycle $_\n";
            sleep 2;
        }
        exit(0);
    }
    else {
        $SIG{CHLD} = \&reaper;
    }
};

$thrd->detach();
sleep 4;
my $cpid = `cat 1`;
kill "TERM", $cpid;
while (1) {}

sub child_exit {
    print "child $$ exits!\n";
    exit(0);
}

sub reaper {
    my $pid;
    while (($pid = waitpid(-1, &WNOHANG)) > 0) {
        print "reaping child process $pid\n";
    }
}

Any suggestions about how to successfully and safely send signal in this situation?

2

2 Answers

2
votes

Why are you saying that the SIGTERM handler does not work? Because the child becomes a zombie?

All children process become zombies unless you wait for them. Put waitpid($pid, 0); after the kill(). Unless you see in child process: cycle 5 in your printout, the kill and the signal handler are working just fine.

Note that it's super shaky to use a hardcoded file to communicate between your forked process and your main process. I'd recommend you use a pipe.

Edit:

Wrt your sig handler not being called, I think this is a perl bug. perl sends signals only to the main thread. When you fork(), your thread becomes the main thread but I think perl does not realize that. You can work this around though by re-forwarding the signal to yourself.

Before you create the threads, just add:

sub sigforwarder {
    threads->self()->kill(shift);
}
$SIG{TERM} = \&sigforwarder;

That should fix your problem

0
votes

I think the problem is, that child_exit does not perform any exit on the parent thread. It just exits the child. I don't know exactly what's happening in perl (just stumbled upon a fork/exec-construction in c), but I would try to catch SIG_CHILD in the parent process to detect its termination.

(the message "child $$ exits" is output in the child's 1, so it will not be visible on screen, right?)

EDIT: I just tried your example and I think I got it: You are performing a fork in the child process and check for the pid to be 0 (which is the parent async-Process). You might just check for != 0.