目前想到有两个办法。
一个是fork方式。
发现直接通过进程的alarm信号方式无法控制proc_open的执行。所以用fork方式,通过fork让子进程执行proc_open,父进程则通过alarm的方式控制时间。但是这么做会造成如果有输出值的话会很麻烦,要让子进程一直执行下去,因为没找到好的办法让父进程能获取子进程的输出。当然通过进程间通信能解决,但是太麻烦了。。。
另外个stream_select方式
- //cmd为要执行的程序, timeout是超时时间
- $descriptorspec = array(
- 0 => array("pipe", "r"),
- 1 => array("pipe", "w"),
- 2 => array("pipe", "w")
- );
- $process = proc_open($cmd, $descriptorspec, $pipes);
- $stdout_str = $stderr_str = "";
- if (is_resource($process)) {
- //执行成功
- stream_set_blocking($pipes[1], 0);
- stream_set_blocking($pipes[2], 0); //不阻塞
- fwrite($pipes[0], $stdin_str);
- fclose($pipes[0]);
- //设置超时时钟
- $endtime = time() + $timeout;
- do {
- $timeleft = $endtime - time();
- $ret = stream_select(
- $read = array($pipes[1],$pipes[2]),
- $write = null,
- $except = null,
- $timeleft
- );
- if ($ret === false) {
- $err = true;
- break;
- } else if ($ret === 0) {
- $timeleft = 0;
- break;
- } else {
- foreach ($read as $sock) {
- if ($sock === $pipes[1]) {
- $stdout_str .= fread($sock, 4096);
- } else if ($sock === $pipes[2]) {
- $stderr_str .= fread($sock, 4096);
- }
- }
- }
- }while((feof($pipes[1]) === false || feof($pipes[2]) === false) &&$timeleft > 0);
- fclose($pipes[1]);
- fclose($pipes[2]);
- if($timeleft <= 0) {
- proc_terminate($process);
- $stderr_str = "操作已超时(".$timeout."秒)";
- }
- if (isset($err) && $err === true){ //这种情况的出现是通过信号发送中断时产生
- proc_terminate($process);
- $stderr_str = "操作被用户取消";
- }
- proc_close($process);
- return true;
- }
- else {
- return false;
- }
stream_select 果然很强大…异步的读取输出,解决执行超时的问题。另外这种方式还可以防止输出堵死缓存。例如如果将上述获取输出的代码改成
- // stdout
- $stdout = stream_get_contents($pipes[1]);
- fclose($pipes[1]);
- // stderr
- $stderr = stream_get_contents($pipes[2]);
- fclose($pipes[2]);
有就可能造成缓存堵死,当然对这种情况的处理也可以通过fork子进程解决。