1

I have a perl script with multiple child processes (created using fork()). Each child sleeps then does some processing (several system() calls). Under windows when I hit Ctrl+C I see that the signal was caught , then the script waits for the children to complete and after this exits. I want to replicate this behaviour in linux (catch sigint , let the children complete and exit the main script/process ).

How do I wait for all children to complete before killing the parent ? Is it possible to exit the main script / kill the parent , but let the children complete in the background ?

I found Child process receives parent's SIGINT , and it seems SIGINT is propagated to the children (the process group) , so to avoid this I'd have to set the child in a different process group.

$SIG{'INT'} = sub {
    print "Caught Ctrl+C\n";
    die "Stopping...";
};

++$|;

print "Start\n";
for($i=0; $i < 500; $i++) {
    sleep 3;
    $childPid = fork(); #fork a process
    if($childPid == 0){
        print "Child $i...\n";
        sleep 10;
        system("echo finishing");
        print "Exit child $i\n";
        exit;
    }
}

I also tried to include this in the SIGINT processing block , but the for loop isn't paused , so new child processes are created and parent doesn't die.

# I used my $parentPid = $$; (before  the for loop) to get the parent pid.

if ($$ = $parentPid){
    foreach $var (@pids) {
        $pid = waitpid($var ,0);
    }  

EDIT:

Tried setting the child SIGINT handler to IGNORE and it works as expected. In the parent handler I do :

$SIG{'INT'} = sub {
  print "Caught Ctrl+C by $$\n";
  # while($result != -1){
      # $result = wait();
  #}
    die "Stopping...";
}; 

a) If include the commented lines the script waits for the children to complete and then exits.

b) But if I comment the "wait" lines I assume it works like this

  1. the script exits and the parent process is terminated
  2. the children continue running in the background and display "Exit child" message as expected before exiting.

I understand the a) case but how does the b) case work, please explain why doesn't this generate zombie processes ?

Community
  • 1
  • 1
Natasha
  • 1,470
  • 5
  • 18
  • 24

1 Answers1

0

Under windows when I hit Ctrl+C I see that the signal was caught , then the script waits for the children to complete and after this exits.

I think this is a side-effect of the psuedo-fork implementation.

How do I wait for all children to complete before killing the parent ?

The easiest way is to ignore the SIGINT In the children, and wait/waitpid in the parents SIGINT handler.

I also tried to include this in the SIGINT processing block , but the for loop isn't paused , so new child processes are created and parent doesn't die.

I think you meant $$ == $parentPid (comparison instead of assignment). Also, it's not really useful to specify which child you're waiting for if you're waiting for all of them anyway.

Leon Timmermans
  • 30,029
  • 2
  • 61
  • 110
  • Yep , my mistake with the "==" , now the parent exits , and I do get the "Exit child" messages , but immediately , maybe the sleep process in the children is terminated , and the rest of the code continues ? I'd like for them to keep sleeping as instructed... And regarding wait ... should I use wait() - will this wait for all children? – Natasha Nov 04 '15 at 11:38
  • Yes, signals interrupt sleeping, which is why you should set the `SIGINT` handler to ignore in the children (`$SIG{INT} = 'IGNORE'`) – Leon Timmermans Nov 04 '15 at 12:18
  • Tried IGNORE for child processes and it works , but I'm still not clear about the "waiting". Please look at the edit – Natasha Nov 04 '15 at 13:35
  • You should keep wait()ing until it returns -1 – Leon Timmermans Nov 04 '15 at 13:56
  • Yes , I do it like that...But I don't understand what's happening if the children ignore SIGINT and the parent exits with "die" without waiting – Natasha Nov 06 '15 at 06:26