user3909192
user3909192

Reputation:

Perl kill child process also exits parent?

I'm writing an emulation stub in PERL to test another program. Below, the code runs a loop that checks for commands (initialize, exit, start_trace, stop_trace). When the "start_trace" command is read, it forks a process that just spits out numbers every second. I want to use "stop_trace" to kill the child, but it kills the parent, too. What am I missing?

use warnings;
use strict;

my $pid = undef;

$| = 1;

$SIG{TERM} = sub { 
    if ($pid == 0) {
        print "Parent, not terminating\n";
    } else {
        print "Child ($pid) terminating\n"; 
        exit(1);
    }
};


print "entering loop\n";
while (1) {
    while (<>) {
        my ($command, @args) = split();
        if ($command eq "exit") {
            exit 1;
        }
        elsif ($command eq "initialize") {
            print "s: ok\n";
        }
        elsif ($command eq "start_trace") {
            if (defined $pid) {
                print "child already running\n";
            } else {
                $pid = fork();
                if ($pid == -1) {
                    print "failed to fork\n";
                    exit 1;
                }
                elsif ($pid == 0) {
                    print "Parent\n";
                }
                else {
                    my $timestamp = 0;
                    while (1) {
                        for (my $i = 0; $i < 12; ++$i) {
                            printf "%.3f %.0f %.2f %.1f\n", 
                                ++$timestamp,
                                0,
                                0,
                                100 * rand()
                            ;
                        }
                        sleep(1);
                    }
                }
            }
        }
        elsif ($command eq "stop_trace") {
            kill "TERM", $pid;
            #waitpid($pid, 0);
            $pid = undef;
        }
        else {
            printf "s: unknown command $command\n";
        }
    }
}

Output (not stdin & stdout are all mixed together, but I'm typing "stop_trace")

stop85.000 0 0.00 66.6
86.000 0 0.00 43.3
87.000 0 0.00 82.3
88.000 0 0.00 62.8
89.000 0 0.00 43.5
90.000 0 0.00 50.0
91.000 0 0.00 8.8
92.000 0 0.00 89.3
93.000 0 0.00 61.4
94.000 0 0.00 92.4
95.000 0 0.00 46.6
96.000 0 0.00 53.9
_trace
Child (26644) terminating
Parent, not terminating
%

But they both exited! Why?

Upvotes: 3

Views: 1375

Answers (1)

Sobrique
Sobrique

Reputation: 53508

I think there's a logic error here - when you fork() what happens is two bits of code branch - and the only difference at that point is that the child process has a return code from fork() of zero, and he parent has a return code of $pid.

It returns the child pid to the parent process, 0 to the child process, or undef if the fork is unsuccessful.

So I think your logic is backwards when you test:

elsif ($pid == 0) {
    print "Parent\n";
}

It's actually your parent that's entering that while loop, and your child process that's still reading stdin on a while loop.

And I Think that's why you're having a problem - because the child is picking up the stop_trace and issuing kill "TERM", 0 rather than the child pid it should be killing.

I'm not actually entirely sure what should be happening when you kill pid 0. But by the looks of it - it's signalling both processes anyway.

And you have the same logic error in your signal handler - your parent is killed, because it has $pid set to 0. I'm not 100% sure why that's causing your child to exit though - are you sure it actually is, and you're not accumulating stale processes sitting in while(1) with a closed STDIN?

Easy enough to check though - stick a print "$$: killing child with pid $pid\n"; in that stop_trace branch.

Oh - and fork in perl returns undef not -1 if it failed.

Upvotes: 3

Related Questions