PHP腳本與系統(tǒng)命令之間通信是常見的情況,exec()函數(shù)也就相應成了PHP中執(zhí)行系統(tǒng)命令的一種常用方法。然而,當執(zhí)行較耗時的系統(tǒng)命令時,腳本執(zhí)行阻塞的問題就會出現(xiàn)。本文將深入探討PHP exec的阻塞現(xiàn)象,及其解決方法。
對于一個簡單的exec()例子,如下:
當exec()函數(shù)執(zhí)行一個較短命令時,比如返回一個目錄下的文件列表,該函數(shù)的運行時間是非常短的,可以忽略不計。但是,當需要執(zhí)行的系統(tǒng)命令耗費時間較長,比如壓縮大文件、重命名大文件、復制大文件等,就可能會導致PHP exec阻塞,進而影響服務器的性能及穩(wěn)定性。
執(zhí)行一個耗時的命令會導致PHP exec阻塞的原因在于exec()默認是同步執(zhí)行的。也就是說,當PHP執(zhí)行exec()函數(shù)時,PHP進程會阻塞等待系統(tǒng)命令執(zhí)行完成,并獲取所有命令的輸出結果后,才會將結果返回給調用程序。
下面我們引用一個例子進行說明,例如我們要使用exec()執(zhí)行一個8G的MySQL備份命令:
/backup/dbname.sql'); ?>
由于備份文件較大,執(zhí)行該命令需要很長時間,而exec()會占滿PHP進程,導致腳本阻塞。當文件增大至幾十GB時,腳本執(zhí)行的時間會更加急劇增加,甚至可能會因為內存或超時的原因造成腳本崩潰。
現(xiàn)在有兩種主流解決方法,一種是使用異步執(zhí)行如proc_open(),另一種是使用php自帶的并發(fā)擴展擴展函數(shù)如pcntl_fork()等實現(xiàn)異步操作。
下面我們舉例說明使用proc_open()函數(shù)解決PHP exec阻塞的問題:
array('pipe', 'r'), // 標準輸入,子進程從此管道中讀取數(shù)據(jù) 1 =>array('pipe', 'w'), // 標準輸出,子進程向此管道中寫入數(shù)據(jù) 2 =>array('pipe', 'w'), // 標準錯誤,子進程向此管道中寫入錯誤數(shù)據(jù) ); $process = proc_open('ls -l', $descriptorspec, $pipes); if (is_resource($process)) { fclose($pipes[0]); // 關閉標準輸入管道,避免將數(shù)據(jù)發(fā)送到子進程 $output = stream_get_contents($pipes[1]); // 讀取標準輸出 fclose($pipes[1]); // 關閉標準輸出管道 $errors = stream_get_contents($pipes[2]); // 讀取標準錯誤輸出 fclose($pipes[2]); // 關閉標準錯誤管道 $return_value = proc_close($process); // 獲取子進程返回值 } ?>
proc_open()函數(shù)允許我們執(zhí)行一個子進程,并通過管道進行輸入輸出,實現(xiàn)子進程與主進程之間互不阻塞,PHP exec阻塞的問題也不用擔心了。
在異步操作方面,PHP自身也提供了多種方案供我們選擇。可以使用pcntl_fork()函數(shù)實現(xiàn)多進程操作;也可以使用cURL、file_get_contents()等函數(shù)實現(xiàn)異步I/O操作,提高PHP腳本與其他文件或服務之間的I/O通信效率。
總結:PHP exec阻塞問題的解決方法多種多樣,可根據(jù)不同需求選擇不同的解決方案。不管使用哪種解決方案,都應根據(jù)實際需要選擇一種最優(yōu)的方法,以達到最高的效率和穩(wěn)定性。