Sunday, May 1, 2011

How can I kill Perl 'system' calls when the main script is killed?

Pervious answers to this questions have focused on forks:

For this question, I'm just asking about calls to the 'system' function.

Say I have a script called sleep.pl:

use strict;
use warnings;
sleep(300);

I then have a script called kill.pl

use strict;
use warnings;
system("sleep.pl");

I run kill.pl and using ps I find the process id of kill.pl and kill it (not using kill -9, just normal kill)

sleep.pl is still sleeping.

I imagine the solution to my question involves a SIG handler, but what do I need to put into the handler to kill the child process?

From stackoverflow
  • Use setsid to make your process the new group leader. Then you can send a kill to the group ID and kill all processes that belong to the group. All processes that you spawn from the leader process inherit the group ID and belong to your newly created group. So sending a kill to the group will kill them all. The only tricky thing is in order to be able to use setsid you must close your standard in and output, as that is a requirement for setsid.

  • use strict;
    use warnings;
    setpgrp $$, 0;
    system("sleep.pl");
    END {kill 15, -$$}
    

    But if you need this approach you do something wrong. You should not do this. Run and kill your kill process in right way instead.

    $ perl -e 'system("sleep 100")' &
    [1] 11928
    $ ps f
      PID TTY      STAT   TIME COMMAND
     4564 pts/1    Ss     0:01 /bin/bash
    11928 pts/1    S      0:00  \_ perl -e system("sleep 100")
    11929 pts/1    S      0:00  |   \_ sleep 100
    11936 pts/1    R+     0:00  \_ ps f
    $ kill %1
    [1]+  Terminated              perl -e 'system("sleep 100")'
    $ ps f
      PID TTY      STAT   TIME COMMAND
     4564 pts/1    Rs     0:01 /bin/bash
    11949 pts/1    R+     0:00  \_ ps f
    

    How it works? Shell (bash in mine case) should set your process as group leader if you run on background. Then if you use kill %? syntax shell kills group in right way. Compare this:

    $ perl -e 'system("sleep 100")' &
    [1] 12109
    $ ps f
      PID TTY      STAT   TIME COMMAND
     4564 pts/1    Rs     0:01 /bin/bash
    12109 pts/1    S      0:00  \_ perl -e system("sleep 100")
    12113 pts/1    S      0:00  |   \_ sleep 100
    12114 pts/1    R+     0:00  \_ ps f
    $ kill 12109
    [1]+  Terminated              perl -e 'system("sleep 100")'
    $ ps f
      PID TTY      STAT   TIME COMMAND
     4564 pts/1    Ss     0:01 /bin/bash
    12124 pts/1    R+     0:00  \_ ps f
    12113 pts/1    S      0:00 sleep 100
    

    But kill %? works in this way:

    $ perl -e 'system("sleep 100")' &
    [1] 12126
    $ ps f
      PID TTY      STAT   TIME COMMAND
     4564 pts/1    Rs     0:01 /bin/bash
    12126 pts/1    S      0:00  \_ perl -e system("sleep 100")
    12127 pts/1    S      0:00  |   \_ sleep 100
    12128 pts/1    R+     0:00  \_ ps f
    $ kill -12126
    [1]+  Terminated              perl -e 'system("sleep 100")'
    $ ps f
      PID TTY      STAT   TIME COMMAND
     4564 pts/1    Ss     0:01 /bin/bash
    12130 pts/1    R+     0:00  \_ ps f
    
    Dennis Williamson : In summary, use a negative PID.
  • Your question really is a specific instance of the more general "how to ensure a child dies when the parent dies" question. There's just no getting around that. There's nothing really special about system(); it just forks a child process and waits for it to exit.

    system() is roughly this:

    sub system 
    {
        my $kidpid = fork;
        if ( $kidpid )
        {
            waitpid $kidpid; 
              # parent process blocks here waiting for the child process to return
        }
        else
        {
            exec( @_ );
        }
    }
    

    Killing the process group is about your only option.

0 comments:

Post a Comment

Note: Only a member of this blog may post a comment.