1

您好,提前感谢您的关注。

在过去的两周里,我一直在与一些让我发疯的事情作斗争。我在我的 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.exereturn 0;在其main()例程中有效地达到了通常的语句,所以它似乎是在 C 运行时中的木头下的东西。此外,如果我编写一个简单的 mytask.exe,它只是休眠 10 秒,那么 PHP 脚本也会阻塞 10 秒(或者在我刚刚提到的随机行为之后永远阻塞)。换句话说,当我从 PHP 脚本调用 job.exe 时,我无法让 job.exe 生成一个进程而不等待它结束。

所以:在生成 mytask.exe 时我做错了,现在,这是题外话的第二部分。

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

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

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

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

4

1 回答 1

3

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

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 脚本随机挂起的事实

这似乎是PHP的一个错误。在 PHP 会话的上下文中运行exec()族函数可能会使 APACHE 随机挂起,这是重新启动服务器所必需的。我在 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

希望这可以帮助。

于 2012-12-04T07:52:58.083 回答