Kill a hung child process

前端 未结 3 1856
醉酒成梦
醉酒成梦 2021-01-13 09:38

My Perl script runs an external program (which takes a single command-line parameter) and processes its output. Originally, I was doing this:

my @result = `p         


        
相关标签:
3条回答
  • 2021-01-13 09:55

    This is the best I could do. Any ideas on how to avoid the use of a temporary file on Windows would be appreciated.

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use File::Temp;
    use Win32::Process qw(STILL_ACTIVE NORMAL_PRIORITY_CLASS);
    
    my $pid;
    my $timeout = 10;
    my $prog = "prog arg";
    my @output;
    
    if ($^O eq "MSWin32")
    {
        my $exitcode;
        my $fh = File::Temp->new ();
        my $output_file = $fh->filename;
        close ($fh);
        open (OLDOUT, ">&STDOUT");
        open (STDOUT, ">$output_file" ) || die ("Unable to redirect STDOUT to $output_file.\n");
        Win32::Process::Create ($pid, $^X, $prog, 1, NORMAL_PRIORITY_CLASS, '.') or die Win32::FormatMessage (Win32::GetLastError ());
        for (1 .. $timeout)
        {
            $pid->GetExitCode ($exitcode);
            last if ($exitcode != STILL_ACTIVE);
            sleep 1;
        }
        $pid->GetExitCode ($exitcode);
        $pid->Kill (0) or die "Cannot kill '$pid'" if ($exitcode == STILL_ACTIVE);
        close (STDOUT);
        open (STDOUT, ">&OLDOUT");
        close (OLDOUT);
        open (FILE, "<$output_file");
        push @output, $_ while (<FILE>);
        close (FILE);
    }
    else
    {
        $pid = open my $proc, "-|", $prog;
        exec ($^X, "-e", "sleep 1, kill (0, $pid) || exit for 1..$timeout; kill -9, $pid") unless (fork ());
        push @output, $_ while (<$proc>);
        close ($proc);
    }
    print "Output:\n";
    print @output;
    
    0 讨论(0)
  • 2021-01-13 10:01

    Try the poor man's alarm

    my $pid;
    if ($^O eq 'MSWin32') {
        $pid = system 1, "prog arg";    # Win32 only, run proc in background
    } else {
        $pid = fork();
        if (defined($pid) && $pid == 0) {
            exec("proc arg");
        }
    }
    
    my $poor_mans_alarm = "sleep 1,kill(0,$pid)||exit for 1..$TIMEOUT;kill -9,$pid";
    system($^X, "-e", $poor_mans_alarm);
    

    The poor man's alarm runs in a separate process. Every second, it checks whether the process with identifier $pid is still alive. If the process isn't alive, the alarm process exits. If the process is still alive after $time seconds, it sends a kill signal to the process (I used 9 to make it untrappable and -9 to take out the whole subprocess tree, your needs may vary. kill 9,... is also portable).

    Edit: How do you capture the output of the process with the poor man's alarm? Not with backticks -- then you can't get the process id and you may lose the intermediate output if the process times out and gets killed. The alternatives are

    1) send output to a file, read the file when the process is done

    $pid = system 1, "proc arg > some_file";
    ... start poor man's alarm, wait for program to finish ...
    open my $fh, '<', 'some_file';
    my @process_output = <$fh>;
    ...
    

    2) use Perl's open to start the process

    $pid = open my $proc, '-|', 'proc arg';
    if (fork() == 0) {
        # run poor man's alarm in a background process
        exec($^X, '-e', "sleep 1,kill 0,$pid||exit ...");
    }
    my @process_output = ();
    while (<$proc>) {
       push @process_output, $_;
    }
    

    The while loop will end when the process ends, either naturally or unnaturally.

    0 讨论(0)
  • 2021-01-13 10:04

    You may want to use alarm system call as in perldoc -f alarm.

    0 讨论(0)
提交回复
热议问题