2012-12-03 66 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 

希望這會有所幫助。