Php 带超时的exec()

Php 带超时的exec(),php,timeout,exec,Php,Timeout,Exec,我正在寻找一种在超时情况下运行PHP进程的方法。目前我只是使用,但它不提供超时选项 我还尝试了在生成的管道上使用和打开流程,但这也不起作用 那么,有没有办法在超时的情况下运行命令(确切地说是PHP命令)?(注:这适用于max\u execution\u time限制失败的情况,因此无需建议。) (顺便说一下,我还需要检索进程的返回代码。)您可以在一个进程中使用fork(),然后在另一个进程中使用exec()非阻塞。另外,还要跟踪超时情况,如果另一个进程没有及时完成,还要跟踪它。我在php.net

我正在寻找一种在超时情况下运行PHP进程的方法。目前我只是使用,但它不提供超时选项

我还尝试了在生成的管道上使用和打开流程,但这也不起作用

那么,有没有办法在超时的情况下运行命令(确切地说是PHP命令)?(注:这适用于
max\u execution\u time
限制失败的情况,因此无需建议。)


(顺便说一下,我还需要检索进程的返回代码。)

您可以在一个进程中使用
fork()
,然后在另一个进程中使用
exec()
非阻塞。另外,还要跟踪超时情况,如果另一个进程没有及时完成,还要跟踪它。

我在php.net上找到了这个,我想它可以做你想做的事情

<?php 
function PsExecute($command, $timeout = 60, $sleep = 2) { 
    // First, execute the process, get the process ID 

    $pid = PsExec($command); 

    if( $pid === false ) 
        return false; 

    $cur = 0; 
    // Second, loop for $timeout seconds checking if process is running 
    while( $cur < $timeout ) { 
        sleep($sleep); 
        $cur += $sleep; 
        // If process is no longer running, return true; 

       echo "\n ---- $cur ------ \n"; 

        if( !PsExists($pid) ) 
            return true; // Process must have exited, success! 
    } 

    // If process is still running after timeout, kill the process and return false 
    PsKill($pid); 
    return false; 
} 

function PsExec($commandJob) { 

    $command = $commandJob.' > /dev/null 2>&1 & echo $!'; 
    exec($command ,$op); 
    $pid = (int)$op[0]; 

    if($pid!="") return $pid; 

    return false; 
} 

function PsExists($pid) { 

    exec("ps ax | grep $pid 2>&1", $output); 

    while( list(,$row) = each($output) ) { 

            $row_array = explode(" ", $row); 
            $check_pid = $row_array[0]; 

            if($pid == $check_pid) { 
                    return true; 
            } 

    } 

    return false; 
} 

function PsKill($pid) { 
    exec("kill -9 $pid", $output); 
} 
?>

我搜索了一下这个主题,得出结论,在某些情况下(如果您使用的是linux),您可以使用“timeout”命令。它很灵活

Usage: timeout [OPTION] DURATION COMMAND [ARG]...
  or:  timeout [OPTION]
在我的特殊情况下,我试图从PHP运行sphinx indexer,这有点像迁移数据脚本,所以我需要重新编制sphinx文档的索引

exec("timeout {$time} indexer --rotate --all", $output);
然后我将分析输出并决定再试一次,或者抛出异常并退出脚本。

(免责声明:我惊讶地发现没有很好的解决方案,然后我浏览了proc文档,发现它非常简单。因此,这里有一个简单的proc答案,它以提供一致结果的方式使用本机函数。您还可以捕获输出以用于日志记录。)

proc函数行具有
proc\u terminate(processhandler)
,它与
proc\u get\u status(processhandler)
相结合,获得“running”键,您可以在使用sleep循环的同时执行超时的同步exec调用

所以基本上:

$ps = popen('cmd');
$timeout = 5; //5 seconds
$starttime = time();
while(time() < $starttime + $timeout) //until the current time is greater than our start time, plus the timeout
{
    $status = proc_get_status($ps);
    if($status['running'])
        sleep(1);
    else
        return true; //command completed :)
}

proc_terminate($ps);
return false; //command timed out :(
$ps=popen('cmd');
$timeout=5;//5秒
$starttime=时间();
while(time()<$starttime+$timeout)//直到当前时间大于开始时间加上超时
{
$status=proc\u get\u status($ps);
如果($status['running']))
睡眠(1);
其他的
返回true;//命令已完成:)
}
程序终止($ps);
返回false//命令超时:(

从PHP脚本调用
timeout{$time}命令时,
解决方案无法正常工作。在我的情况下,如果向错误的服务器发出ssh命令(找不到rsa密钥,服务器请求密码),该进程在定义的超时后仍处于活动状态

但是,我在这里发现了一个可以正常工作的函数:

C&p:


我面临的问题与我尝试过上述所有答案的问题相同,但windows server无法处理其中任何一个问题,可能是我的愚蠢

我的windows最终工作解决方案是执行批处理文件

timeout.bat

::param 1 is timeout seconds, param 2 is executable
echo "running %2 with timeout %1"
start %2
set time=0

:check
tasklist /FI "IMAGENAME eq %2" 2>NUL | find /I /N "%2">NUL
::time limit exceed
if "%time%"=="%1" goto kill
::program is running
if "%ERRORLEVEL%"=="0" ( ping 127.0.0.1 -n 2 >nul & set /a time=%time%+1 & goto check) else ( goto end)

:kill
echo "terminate"
taskkill /im %2 /f

:end
echo "end"
php命令

exec("timeout.bat {$time} your_program.exe");

改进其他解决方案我提出了以下建议:

function exec_timeout($cmd,$timeout=60){
        $start=time();
        $outfile=uniqid('/tmp/out',1);
        $pid=trim(shell_exec("$cmd >$outfile 2>&1 & echo $!"));
        if(empty($pid)) return false;
        while(1){
                if(time()-$start>$timeout){
                        exec("kill -9 $pid",$null);
                        break;
                }
                $exists=trim(shell_exec("ps -p $pid -o pid="));
                if(empty($exists)) break;
                sleep(1);
        }
        $output=file_get_contents($outfile);
        unlink($outfile);
        return $output;
}

启动计时器。将进程放入无限循环,检查计时器,在需要时超时。你是指这里的pcntl_uu函数吗?@NikiC:是的,我认为这是PHP中所称的。这看起来是一种合理的方法,但在这种情况下如何获得返回代码?但手册上说proc_get_ustatus和proc_terminate与resource ret一起工作仅由proc_open启动,而不是popen?运行良好:)为了获得更高的精度,您可以使用microtime(true)替换time(),还可以使用较小的时间间隔(如usleep(100000))进行100毫秒的睡眠。我发现这段代码有一个问题。虽然对
stream\u get\u contents($pipes[1])
的调用确实会立即返回,因为该管道被设置为非阻塞,但最后一个调用
stream\u get\u contents($pipes[2])
实际上会阻塞,直到整个进程退出,因为stderr管道从未设置为非阻塞。这违背了在超时后将控制权返回给调用方的全部目的。解决方案很简单:添加
stream\u set\u阻塞($pipes[2],0)在第一个之后above@juanra:在PHP脚本中使用
timeout{$time}命令时,我没有遇到任何问题。你看到什么问题了?可能是诸如
最大执行时间
不足之类的问题导致了问题?
function exec_timeout($cmd,$timeout=60){
        $start=time();
        $outfile=uniqid('/tmp/out',1);
        $pid=trim(shell_exec("$cmd >$outfile 2>&1 & echo $!"));
        if(empty($pid)) return false;
        while(1){
                if(time()-$start>$timeout){
                        exec("kill -9 $pid",$null);
                        break;
                }
                $exists=trim(shell_exec("ps -p $pid -o pid="));
                if(empty($exists)) break;
                sleep(1);
        }
        $output=file_get_contents($outfile);
        unlink($outfile);
        return $output;
}