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

本文介绍了一种解决PHP在Windows下调用外部程序超时导致的资源耗尽问题的方法。通过编写一个名为process.exe的代理程序,可以设定调用的客户程序的最大运行时间,当超过该时间限制时,代理程序会自动终止进程,从而避免资源阻塞。文中给出了PHP调用示例和代理程序的C++源代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我近期做一个东东,大致构架是:
访问者通过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;
}

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值