2011-12-12 46 views
2

我试图在使用system()的Perl脚本中运行应用程序。我正在运行的应用程序有时会卡住(它会进入某种无限循环)。有没有一种方法可以让我知道这个应用程序是否被卡住并杀死它继续使用Perl脚本?在Perl中使用system()启动应用程序开始

我试图做这样的事情:

start testapp.exe; 
if(stuck with testapp.exe) { 
    kill testapp.exe; 
} 
+0

“有没有一种方法可以让我知道......”......其实,这听起来好像你有某种方式知道它“卡住”了。你是否决定用手?基于运行时间过长的程序,或者反复输出某个输出,或者...? – BRFennPocock

回答

2

确定“它是否陷入无限循环”被称为Halting Problem并且是不可判定的。

如果你想杀死它,你将不得不使用fork来分割应用程序,如果它太长了,就从另一个分支中杀掉它。

您可以确定proccess会太久本

use POSIX ":sys_wait_h"; 
waitpid($pid, WNOHANG)>0 #waitpid returns 0 if it still running 

至少,根据this manual page

我不知道它有多好作品在各种系统上,你可以尝试出。

不是一个直接的答案,但我可以推荐使用forks模块,如果你想轻松到餐桌,但它的工作原理在UNIX系统(窗口)。


OK,更有助于代码:)它的工作原理在UNIX,根据perlfork的perldoc,它应该工作在Windows上完全相同的方式。

use warnings; 
use strict; 

use POSIX ":sys_wait_h"; 
my $exited_cleanly;     #to this variable I will save the info about exiting 

my $pid = fork; 
if (!$pid) { 
    system("anything_long.exe");  #your long program 
} else { 
    sleep 10;       #wait 10 seconds (can be longer) 
    my $result = waitpid(-1, WNOHANG); #here will be the result 

    if ($result==0) {     #system is still running 
     $exited_cleanly = 0;   #I already know I had to kill it 
     kill('TERM', $pid);    #kill it with TERM ("cleaner") first 
     sleep(1);      #wait a bit if it ends 
     my $result_term = waitpid(-1, WNOHANG); 
             #did it end? 

     if ($result_term == 0) {  #if it still didnt... 
      kill('KILL', $pid);   #kill it with full force! 
     } 
    } else { 
     $exited_cleanly = 1;   #it exited cleanly 
    } 
} 

#you can now say something to the user, for example 
if (!$exited_cleanly) {...} 
+0

这就是我想要对执行做一些时间限制。有没有办法找到子进程是否仍在运行? – int80h

+0

我已添加更多信息。告诉我它是如何工作:) –

+0

非常感谢! :)这是有帮助的。 – int80h

0

您不能确定,如果你执行它像应用程序被套牢,因为system语句不会返回,直到应用程序终止。

至少,您需要启动测试应用程序,以便它可以从要监视它的Perl脚本异步运行。

解决了这部分问题后,您必须建立一种机制,允许监视Perl脚本确定应用程序被卡住。这是一个非平凡的练习,并且可能依赖于系统,除非您采用简单的权宜之计,例如要求应用程序在某处写入心跳指示,并且Perl脚本监视心跳。例如(不一定是一个很好的例子),应用程序可以将当前时间写入由PID标识的文件中,并且Perl脚本可以监视该文件以查看心跳是否足够近。当然,这假定'无限循环'不包括写入心跳文件的代码。

+0

那么你可以建议一些其他的方法来启动应用程序,可以帮助我确定应用程序的状态吗? – int80h

+0

标准方法是使用'fork'并让子'exec'为应用程序,而父代监视孩子。 OTOH,这是微不足道的;从外部确定测试应用程序内部发生了什么是很难的。 –

+3

暂停问题的一般解决方案对此非常有用;-) – BRFennPocock

1
system("start testapp") 

是短期的

system("cmd", "/c", "start testapp") 

的Perl只是知道cmd;它不知道任何关于start,更不用说关于testappsystem不是你想要的工具。这是第一个问题。


第二个问题是,你还没有定义什么意思是“卡住”。如果你想监视一个程序,它需要一个心跳。心跳是可以从外部检查的周期性活动。它可以写入管道。它可以改变一个文件。任何东西。

监控程序会监听这个心跳,并假定如果心脏停止跳动,程序就会死机,可以这么说。


“被害”在UNIX上使用的信号完成的,但它在Windows中使用TerminateProcess完成。第三个问题是Perl核心不允许你访问该函数。


第一个和第三个问题的解决方案是Win32::Process。它允许你在后台启动一个进程,它也允许你终止它。

创建心跳取决于您。

+1

'TerminateProcess'调用内置于Windows上的perl - 参见'win32/win32.c'。 '杀死'KILL',...'和'杀死'ZERO',...'是Windows上的DWIM。但其他信号并非如此。 – mob

+1

这意味着你可以使用'my $ pid = system(1,“testapp”);'(记录在perlport中)和'kill KILL => $ pid;'而不是Win32 :: Process。 – ikegami

1

如果你知道testapp不应该花费超过N秒的时间来完成它的事情,那么你可以通过这种方法来处理这个问题,然后你可以通过IPC::Run来使用超时来终止应用程序。

在下面的示例中,会有1秒的超时时间,其中杀死sleep 10命令花费的时间太长(超过1秒的超时时间)。如果这不符合你的要求,那么你应该提供更多关于如何检测testapp.exe“卡住”的信息。

#!/usr/bin/env perl 

use IPC::Run qw(run timeout); 

eval { # if (stuck with testapp.exe for more than N seconds) 
    @cmd = ('sleep', '10'); # this could be testapp.exe instead of sleep 
    run \@cmd, \$in, \$out, \$err, timeout(1) or die "test"; # start testapp.exe 
    print "do stuff if cmd succeeds\n"; 
}; 
print "more stuff to do afterwards whether or not command fails or succeeds\n"; 
+1

此解决方案适用于Windows吗? – int80h

+0

我没有在Windows上测试过,但是,这个解决方案应该适用于Windows。 Windows实现有[限制](http://search.cpan.org/~toddr/IPC-Run-0.90/lib/IPC/Run.pm#Win32_LIMITATIONS),但是没有[限制](http:// search.cpan.org/~toddr/IPC-Run-0.90/lib/IPC/Run.pm#Win32_LIMITATIONS)列出应该防止这个简单的超时例子工作。 – aculich

相关问题