windows下如何解决PHP调用的外部程序超时阻塞问题
我近期做一个东东,大致构架是:
访问者通过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;
}
我近期做一个东东,大致构架是:
访问者通过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;
}
windows:
exec("xcopy32/s src_dir di_dir",$str,$var);
linux:
exec("cp -r ........
如果需要复杂交互操作可以使用管道
popen();
或者更加强大的
proc_open();//php4.3.0
<?
//错误处理供调试,可以将错误分布到各个对象内部,但是又提供全局访问能力
$err_code[1]="error 1";
$err_code[2]="error 2";
$err_code[3]="error 3";
$err_code[4]="error 4";
$err_code[5]="error 5";class Info
{
var $type;
var $name;
var $error; function AddError($code)
{
$this->error[]=$code;
} function ShowMyError()
{
global $err_code;
for($i=0;$this->error[$i];$i++)
echo $err_code[$this->error[$i]]."<br>";
}
}
class Obj
{
var $objs;
function ShowAllError()
{
for($i=0;$this->objs[$i];$i++)
{
$tmp=$this->objs[$i];
global $$tmp;
echo $$tmp->info->ShowMyError();
}
}
}
$OBJ=new Obj;
function NewObj($type)
{
$out=new $type;
$info=new Info;
$info->type=$type;
$out->info=$info;
return $out;
}
function MyNew($type,$name)
{
global $$name,$OBJ;
$$name=NewObj($type);
$$name->info->name=$name;
$OBJ->objs[]=$name;
}class derive1
{
function do1()
{
$i=1;
if($i==1)
$this->info->AddError(1);
else
$this->info->AddError(2);
}
}class derive2
{
function do2()
{ $i=2;
if($i==1)
$this->info->AddError(1);
else
$this->info->AddError(2); }
}
///////////////////////////////
MyNew("Derive1","Inst1");
MyNew("Derive1","Inst2");
MyNew("Derive2","Inst3");
$Inst1->do1();
$Inst3->do2();
echo "The following lines list error in Inst1:<br>";
$Inst1->info->ShowMyError();
echo "The following lists all error:<br>";
$OBJ->ShowAllError();
?>
很多中应用可以使用这种模式。
class class_test{
var $sType;
var $sCnt;
function class_test(){
$this->sCnt = &$GLOBALS[get_class($this)]['sCnt'];
$this->sCnt++;
}
}
$t1 = new class_test();
$t2 = new class_test();
$t3 = new class_test();
echo $t1->sCnt."<BR>";
echo $t2->sCnt."<BR>";
echo $t3->sCnt."<BR>";
输出都是3
class class_test{
var $sType;
var $sCnt;
function class_test(){
$this->sCnt = &$GLOBALS[get_class($this)]['sCnt'];
$this->sCnt++;
}
}
$t1 = new class_test();
访问属性sCnt可以用$t1->sCnt
或$t1['sCnt']
在某些特殊情况下用得上,
如果你要清空类某个类属性,就要用到unset($t1['scnt']);
<?php
//////////////////////////////////////////////////////////
////////////简便加入隐藏信息作者:mao_junhua//////////////////
//////////////////////////////////////////////////////////class Info
{
var $type;
var $name;
var $objs;
function Info()
{
$this->objs=&$GLOBALS[get_class($this)]['objs'];
}
function ShowInfo()
{
echo 'My type is: '.$this->type.', My name is: '.$this->name.'<br>';
}
function ShowAllObjInfo()
{
for($i=0;$this->objs[$i];$i++)
{
$tmp=$this->objs[$i];
global $$tmp;
echo $$tmp->info->ShowInfo();
}
}
}function NewObj($type)
{
$out=new $type;
$info=new Info;
$info->type=$type;
$out->info=$info;
return $out;
}
function MyNew($type,$name)
{
global $$name,$OBJ;
$$name=NewObj($type);
$$name->info->name=$name;
$$name->info->objs[]=$name;
}/////////////////////////////////////////////////////////////class derive1
{}class derive2
{
}
///////////////////////////////MyNew("Derive1","Inst1");
MyNew("Derive1","Inst2");
MyNew("Derive2","Inst3");
$Inst1->info->ShowInfo();
$Inst1->info->ShowAllObjInfo();?>
运行结果:
My type is: Derive1, My name is: Inst1
My type is: Derive1, My name is: Inst1
My type is: Derive1, My name is: Inst2
My type is: Derive2, My name is: Inst3
class debug{
/**
* @return void
* @param mixed $var
* @param str $title
* @desc trace
*/
function trace($var, $title = null){
if(!is_null($title)) echo '<b>' . $title . ":</b>\n";
echo "<pre>" . print_r($var, true) . "</pre>\n";
}
/**
* @return void
* @param mixed $var
* @desc Pop values use JavaScript
*/
function alert($var){
if(is_bool($var)) $var = $var ? "true" : "false";
echo "<script>alert(\"" . str_replace("\n", '\n', print_r($var, true)) . "\");</script>\n";
}
}
我的意思不是将文件操作作为基类,而是将某些I/O抽象出来,在基类里面抽象操作,然后再到派生类里面具体实现到文件操作或者是数据库操作等其他方向.因为php无法做到用基类类型指针指向派生类来实现多态,只能绕着弯子来实现,如我1中提到的class base的ShowSelf函数,如果Show操作是所有派生对象共同的需要的操作,则我们可以封装到基类,但是Show中有一个操作ShowSelf是未知的,要根据不同的派生对象特定才能确定,我们就在基类中不定义这个ShowSelf,改在派生类中定义。
不知道vbs在oo方面能做到什么程度.
因为现在做asp.net开发,所以没有对vbs的oo特性做过多研究,
希望"超级大笨狼"等vbs高手能来讲解一下.
<?
class StatVar{
function &setInit(){
static $static = '';
return $static;
}
function set($key,$value){
$var = &StatVar::setInit();
$var[$key] = $value;
}
function &get($key){
$var = &StatVar::setInit();
return $var[$key];
}
}
//例子如下:
StatVar::set('aa', 'bb');
echo StatVar::get('aa');
?>