从PHP执行程序会挂起APACHE

时间:2012-12-03 19:44:39

标签: php c windows createprocess mingw32

您好,并提前感谢您的兴趣。

在过去的两周里,我一直在努力解决让我疯狂的事情。我在我的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在其return 0;例程中有效地达到了通常的main()语句,所以它似乎是在C运行时的基础之下。 此外,如果我写一个简单的mytask.exe只是睡了10秒钟,那么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()...但这也关闭了job.exe的流!

所有这些问题都可以合成为一个问题:如何从PHP中执行创建其他进程的程序?

我希望有人能对这个问题有所了解......非常感谢你!

1 个答案:

答案 0 :(得分: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
);

正如我在问题中所说,我将FALSE传递给bInheritHandles,但我也将0传递给dwCreationFlags。经过一些研究,我发现有一个名为DETACHED_PROCESS的标志,MSDN说:

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

现在,尽管子进程继续执行,但job.exe立即返回。

2)关于调用exec()时PHP脚本随机挂起的事实

这似乎是PHP的一个错误。在PHP会话的上下文中运行exec()系列函数可能会使APACHE随机挂起,这是重启服务器所必需的。我找到了thread in the Internet,其中用户注意到在调用session_write_close()之前关闭会话(通过exec())会阻止脚本挂起。这同样适用于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

希望这有帮助。