您好,并提前感谢您的兴趣。
在过去的两周里,我一直在努力解决让我疯狂的事情。我在我的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中执行创建其他进程的程序?
我希望有人能对这个问题有所了解......非常感谢你!
答案 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
希望这有帮助。