windows下如何解决PHP调用的外部程序超时阻塞问题

原创 2004年06月25日 22:10:00

我近期做一个东东,大致构架是:
访问者通过web提交c程序,服务端调用编译器编译并且在编译完成后运行编译后的程序,将运行结果传回访问者浏览器。
且不考虑安全性,因为访问者可以都认为是可以信赖的,命令行编译器最终要返回的,但是对于临时编译的程序,尽管用户是可以信赖的,但是不排除因为不完善而出现死锁,php调用后启动的进程无法返回而超时,此进程一直存在直到服务器重启,久而久之,服务器端资源就要耗尽。

考虑到php本身执行的时候没有提供多线程和进程管理功能(可能是我没有看到这方面的资料),使用不管是exec,还是popen等,主程序一旦阻塞就无法自拔,所以必须预留一个线程在必要时管理启动的进程.而我又不想对服务器配置做改动。于是想到自己写一个程序管理启动的进程,php间接通过这个程序调用编译后的客户程序,实现对客户程序超时的控制。
下面是测试用的php程序。
<?
//filename: test1.php
$cmd="test.exe 24 154";// input you command here

$cmd="process.exe 5000 ".$cmd;
$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
   2 => array("file", "error-output.txt", "w+"), // stderr is a file to write to
);
$process = proc_open($cmd, $descriptorspec, $pipes);
if (is_resource($process)) {
    // $pipes now looks like this:
    // 0 => writeable handle connected to child stdin
    // 1 => readable handle connected to child stdout
    // Any error output will be appended to /tmp/error-output.txt

    fwrite($pipes[0], '12345678');// input integer to scanf, you should add '/n' at the end of string as 'Enter';

 fclose($pipes[0]);

   
    while(!feof($pipes[1])) {
        echo nl2br(fgets($pipes[1], 1024));
    }
    fclose($pipes[1]);
    // It is important that you close any pipes before calling
    // proc_close in order to avoid a deadlock
 //proc_terminate($process);
    $return_value = proc_close($process);

    echo "<br/>command returned $return_value/n";
}
?>

process.exe就是我编写提供给php的代理程序。
使用方法:
process.exe max_time_limit client_exe_name [ param1_to_client, param2_to_client,... ...]
max_time_limite是以毫秒为单位。

一般情况下,我们使用proc_open()是这样的
$cmd="test.exe 24 154";

通过process.exe间接调用变成:
process.exe 1000 test.exe 24 154
当调用的客户程序超时,process就会杀死进程并返回。

以下是process的源程序,Dev-C++下编译通过。
//filename:process.cpp
#include <iostream>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <string.h>
using namespace std;
DWORD WINAPI ThreadFunc( LPVOID lpParam );
char gargv[255];
bool end_thread=true;
PROCESS_INFORMATION pi;
int main(int argc, char *argv[])
{
      if(argc<2)
      {
      printf("Too few parameters");
      return 1;
      }
   
  strcpy(gargv,argv[2]);
  char blank[]=" ";
  for(int i=3;i<argc;i++)
  {
   strcat(gargv,blank);
   strcat(gargv,argv[i]);
   }
   //printf("%s",gargv); 
  
    DWORD dwThreadId, dwThrdParam = 1;
    HANDLE hThread;
    char szMsg[80];   
    hThread = CreateThread(
        NULL,                        // default security attributes
        0,                           // use default stack size 
        ThreadFunc,                  // thread function
        &dwThrdParam,                // argument to thread function
        0,                           // use default creation flags
        &dwThreadId);                // returns the thread identifier
 
   // Check the return value for success.
 
   if (hThread == NULL)
   {
      wsprintf( szMsg, "CreateThread failed." );
      MessageBox( NULL, szMsg, "main", MB_OK );
   }
   else
   {
     Sleep(500);
     if(end_thread)
     {  
          Sleep(atoi(argv[1]));
          if(pi.hProcess)
               {
                   TerminateThread(pi.hThread ,0);    
                   TerminateProcess(pi.hProcess , 0);
               }
     }
     CloseHandle( hThread );
   }
  return 0;
}
DWORD WINAPI ThreadFunc( LPVOID lpParam )
{
    STARTUPINFO si;
    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    // Start the child process.
    if( !CreateProcess( NULL, // No module name (use command line).
        gargv, // Command line.
        NULL,             // Process handle not inheritable.
        NULL,             // Thread handle not inheritable.
        TRUE,            // Set handle inheritance to FALSE. 关键是这个参数,为TRUE则进程使用父控制台窗口。
        0,                // No creation flags.
        NULL,             // Use parent's environment block.
        NULL,             // Use parent's starting directory.
        &si,              // Pointer to STARTUPINFO structure.
        &pi )             // Pointer to PROCESS_INFORMATION structure.
    )
    {
        return 1;
    }

    // Wait until child process exits.
    WaitForSingleObject( pi.hProcess, INFINITE );
    // Close process and thread handles.
    end_thread=false;
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
}

测试用的客户程序test.cpp:
#include<stdio.h>
int main(int argc,char *argv[])
{
    for(int i=0;i<argc; i++)
    {
        printf("This is from command line: %s /n",argv[i]);
    }
    int j;
    printf("Input a integer: ");
    scanf("%d",&j);
    printf("/n This is from proc_open input: %d",j);
    return 0;
}

 


 

window下nginx+fastcgi+php的并发阻塞问题

看了好多php-fpm的文章以及fastcgi文章,总结几点: 1.fastcgi这个概念有人说是windows提出的 2.windows下,是没有php-fpm的,有的是 php-cgi.e...
  • beyond__devil
  • beyond__devil
  • 2016年11月19日 08:12
  • 1403

PHP 调用系统命令 超时

在PHP中调用外部命令,可以用如下三种方法来实现:   方法一:用PHP提供的专门函数(四个): PHP提供4个专门的执行外部命令的函数:exec(), system(), pa...
  • dodott
  • dodott
  • 2016年03月18日 14:40
  • 516

【干货篇】调用其他系统http接口超时了,如何处理,方案汇总

如果你参加过互联网项目,那么你一定知道,整个大的系统会被切成许多的子系统。子系统与子系统通过接口交互。其中,通过http接口交互,是非常常用的一种方式。那么如果调用某个子系统的http接口超时了,该如...
  • linsongbin1
  • linsongbin1
  • 2015年12月24日 11:33
  • 7015

PHP超时处理总结

概述在PHP开发中工作里非常多使用到超时处理到超时的场合,我说几个场景: 异步获取数据如果某个后端数据源获取不成功则跳过,不影响整个页面展现 为了保证Web服务器不会因为当个页面处理性能差而导致无法访...
  • u013474436
  • u013474436
  • 2016年11月14日 12:31
  • 2389

BufferedReader的readLine方法阻塞的处理

BufferReader的read方法和readLine方法在任何情况下都是阻塞的。readLine方法每次读一行,相对于一个字符/字节地读取、转换、返回来说,它有一个缓冲区,读满缓冲区才返回;一般情...
  • u010595903
  • u010595903
  • 2016年06月15日 10:46
  • 5869

Java调用外部程序命令时线程阻塞问题分析

原文地址:http://www.qqread.com/java/2010/05/w493489.html 今天要写个远程重启服务的功能,为了开发速度,暂时定为Java代码+WMIC命令的方法,简单的...
  • synge33
  • synge33
  • 2012年03月09日 10:26
  • 2064

导致页面打开速度变慢--Session阻塞造成时的解决方案

Asp.net项目因Session阻塞导致页面打开速度变慢      前年有个Asp.net项目上线后,正常情况下大部分页面打开速度都很快,但个别页面处理速度较慢。奇怪的是一旦访问个别速度慢的页...
  • lisky119
  • lisky119
  • 2016年05月20日 17:37
  • 1440

管道piep使用ReadFile读取阻塞问题解决

0x01.CreatePipe管 道(Pipe)实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另 一进程就可以从管道的...
  • ktpd_pro
  • ktpd_pro
  • 2017年04月11日 14:50
  • 2149

nginx php-fpm响应长排查

说明: web页面响应时间长,要怎么排查? 因为是lnmp系统,可以通过设置nginx日志,记录nginx处理请求的时间、开启php慢执行来排查 解决: 一、修改nginx.conf日...
  • joeyon
  • joeyon
  • 2015年11月25日 13:57
  • 1948

JAVA调用外部程序阻塞问题

前些天使用Java调用外部程序的时候,发现线程会堵塞在waitfor()方法。 调用方法如下: Process process = Runtime.getRuntime().exec...
  • qiugongxiang
  • qiugongxiang
  • 2014年04月11日 10:44
  • 721
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:windows下如何解决PHP调用的外部程序超时阻塞问题
举报原因:
原因补充:

(最多只允许输入30个字)