2012-12-03 45 views
1

你好,并提前感谢您的兴趣。执行从PHP程序挂起APACHE

在过去的两个星期里,我一直在苦苦挣扎,我在Windows上安装了APACHE(2.2.22)和PHP(5.4.3),我试图从一个PHP脚本调用另一个程序同时调用的程序。这两个程序都是用C/C++编写的,并用MINGW32编译。关于Windows版本,我测试了Windows 2003 Server和Windows 7 Professional,两者都给我提出了相同的问题。

我来介绍一下这两种方案:

1)mytask.exe:这是一个程序,在后台执行,并定期填充它的状态到一个文件中。

2)job.exe:这是我想从PHP脚本调用的程序。它的目标是产生mytask.exe作为一个独立的进程(而不是一个线程)。

如果我从控制台窗口运行下面的命令,那么job.exe会立即返回并使mytask.exe在后台运行,直到它终止。

> job.exe spawn mytask.exe 
jobid=18874111458879FED 

请注意,job.exe转储用于管理mytask.exe的标识符。例如:

> job.exe status 18874111458879FED 
RUNNING 

我检查了,如果我从一个PHP脚本,PHP脚本随机块永远运行的第一个命令。如果我查看Windows的任务管理器,我可以看到job.exe处于僵尸状态。我可以断言job.exe有效地达到了main()例程中通常的return 0;声明,所以它似乎是C运行时中的一些东西。另外,如果我编写简单的睡眠10秒的简单mytask.exe,那么PHP脚本也会阻塞10秒(或者在我刚刚提到的随机行为之后永远阻塞)。换句话说,当我从PHP脚本调用job.exe时,我无法让job.exe产生一个进程而无需等待它结束。

因此:产生mytask.exe时出现了一些问题,现在,这里是第二部分。

我使用WINAPI函数CreateProcess()从job.exe产生任务。从MSDN文档中,我使用bInheritHandles = FALSE调用CreateProcess,以避免子进程使用PHP脚本产生I/O死锁。我还关闭了PROCESS_INFORMATION结构中由CreateProcess()返回的进程句柄。我唯一不做的就是等待这个过程结束。另一方面,关于PHP方面,我已经尝试了exec()proc_open() PHP函数来调用job.exe没有成功。

虽然我最近的观察结果似乎是正确的,但他们没有说服我,因为我不明白他们为什么要以某种方式工作。事实是,如果mytask.exe在睡眠之前做了fclose(stdout),那么PHP脚本会立即返回。但是,如何?我告诉CreateProcess()不要继承句柄,为什么我会得到这些结果?无论如何,我不能坚持这个补丁,因为job.exe启动的程序可能不知道谁在调用它们,所以从这些程序中关闭stdout不是一个好的解决方案。在UNIX中,事情非常简单...只需调用fork(),关闭标准流,然后调用execve来调用程序。在Windows中,我也尝试使用CreateThread()创建一个包装器线程(以模拟fork()),然后在关闭标准流之后从该线程调用CreateProcess()......但是这封闭了作业流。exe也是!

所有这个问题都可以合成一个:我怎样才能从PHP执行一个创建其他进程的程序?

我希望有人能够对这个问题提出一些看法......非常感谢!

回答

2

我想我已经钉了解决方案,它分为两个部分:

1)关于主要过程停止,直到子进程结束的事实。

由于MSDN文档,这是CreateProcess()定义:

BOOL WINAPI CreateProcess(
    _In_opt_  LPCTSTR lpApplicationName, 
    _Inout_opt_ LPTSTR lpCommandLine, 
    _In_opt_  LPSECURITY_ATTRIBUTES lpProcessAttributes, 
    _In_opt_  LPSECURITY_ATTRIBUTES lpThreadAttributes, 
    _In_   BOOL bInheritHandles, 
    _In_   DWORD dwCreationFlags, 
    _In_opt_  LPVOID lpEnvironment, 
    _In_opt_  LPCTSTR lpCurrentDirectory, 
    _In_   LPSTARTUPINFO lpStartupInfo, 
    _Out_  LPPROCESS_INFORMATION lpProcessInformation 
); 

正如我在我的问题说,我通过FALSEbInheritHandles,但我也路过0dwCreationFlags。在进行了一些更多的研究之后,我发现有一个标志叫做DETACHED_PROCESS,对此,MSDN说:

对于控制台进程,新进程不会继承其父控制台(默认值)。新进程可以在稍后调用AllocConsole函数来创建控制台。有关更多信息,请参阅创建控制台。

现在,即使子进程继续执行,job.exe也会立即返回。

2)关于事实调用exec()

这似乎是PHP的一个bug,当PHP脚本随机挂起。在PHP会话的上下文中运行exec()系列函数可能会使APACHE随机挂起,这对于重新启动服务器是必需的。我发现一个thread in the Internet其中用户注意到在致电exec()之前关闭会话(通过session_write_close())将阻止脚本挂起。这同样适用于proc_open/proc_close函数。所以,我的脚本现在看起来像这样:

session_write_close(); //Close the session before proc_open() 
$proc = proc_open($cmd,$pipedesc,$pipes); 
//do stuff with pipes... 
//... and close pipes 
$retval = proc_close($proc); 
session_start(); //restore session 

希望这会有所帮助。