您好,登錄后才能下訂單哦!
今天小編給大家分享一下PHP怎么實現守護進程的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
其實只需要創建子進程并退出父進程,將要處理的工作在子進程中進行就可以實現一個守護進程了。但是僅僅是這么做的話,如果后續任務很復雜,或者引入了一些第三方包,那么可能就會出現奇奇怪怪的問題了。
而在《UNIX環境高級編程》(英語:Advanced Programming in the UNIX Environment,簡稱APUE)一書中有介紹關于守護進程的編碼規范,我們按照規范來實現我們的守護進程就可以避免出現那些奇怪的問題了。而且規范也不復雜,只需要幾步就可以了:
創建子進程,退出父進程
子進程創建一個新的會話并成為 session leader
重設文件掩碼
改變工作目錄
關閉標準輸入輸出
<?php function daemon() { // [1] 創建子進程 $pid = pcntl_fork(); if ($pid == -1) { die('fork failed'); } // [2] 如果是父進程,則退出 if ($pid > 0) { exit(0); } ///////////////// 以下是子進程 ///////////////// // [3] 創建一個新的會話并成為 session leader if ( ($sid = posix_setsid()) <= 0 ) { die("Set sid failed.\n"); } // [4] 重設文件掩碼 umask(0); // [5] 改變工作目錄 if (chdir('/') === false) { die("chdir failed.\n"); } // [6] 關閉標準輸入輸出 fclose(STDIN); fclose(STDOUT); fclose(STDERR); } daemon(); // ... 真正的處理邏輯
上面短短的十幾二十行代碼就實現了一個守護進程,接下來解釋一下有些步驟為什么要這么做。
pcntl_fork()
的返回值有三種情況,上面的代碼([1]和[2])已經處理了對應的情況。
調用 posix_setsid()
創建新會話會使得當前進程成為新會話中的“會話首進程”,同時也會使當前進程成為“進程組組長”,并且使得當前進程脫離控制終端。
調用 umask()
重設文件掩碼,這里通常是 0。為什么是 0 而不是其他呢,因為子進程從父進程繼承來的文件掩碼可能會屏蔽某些特定的文件操作權限。比如說引入的第三方庫可能需要用特定的權限來創建文件,并且它沒有將文件權限作為一個選項參數由你指定,那么就可能會出現失敗的情況;而我們傳入 0
,會使得從調用了 umask()
之后,守護進程創建的文件權限為 0666
,目錄權限為 0777
,均為最高權限。
關于 umask()
后面會展開新的篇幅來說明,感興趣的可以先自行搜索資料學習。
通過 chdir()
我們將工作目錄設置為根目錄 /
,主要是因為守護進程是長時間運行的,通常只有系統關閉/重啟才會退出。假如從父進程繼承來的工作目錄是個掛載的文件系統,如果不改變工作目錄,那么將會導致這個掛載的文件系統一直沒法卸載。
當然也不一定要將工作目錄切換到根目錄,你也可以根據實際情況切換到特定的目錄。
因為守護進程是脫離終端控制的,所以是沒有標準輸入輸出交互的,我們將其關閉即可。
二次 fork
你可能在一些資料中看到有人推薦你在 [3] 創建一個新的會話并成為 session leader 之后再次進行 fork
。這一步驟是在基于 System V
的系統中,可以保證你的守護進程不是“會話首進程”,可以阻止其重新申請獲取一個控制終端。
關閉不必要的文件描述符
按照編碼規范,實際還有一步是關閉不必要的文件描述符。但我們為了簡單起見,上面的代碼在進程啟動之后先創建守護進程再執行其他操作,因此這里只打開了三個文件描述符: 0
、1
和 2
(即標準輸入、標準輸出、標準錯誤)。
因為上面的代碼將標準輸入輸出關閉了,也就是說如果你在 daemon()
之后有諸如 echo "Hello world";
之類的輸出,那么你的程序將會出錯然后退出,并且你將看不到任何錯誤信息(因為標準錯誤也被關閉了)。
解決方案有兩種,一種是用 file_put_contents
代替 echo
,但是這樣并不優雅,而且萬一引入的第三方包中寫了 echo
或者是 file_put_contents(STDOUT, ...)
,那你的程序也會“莫名其妙”就掛了,會讓你排查半天到底是哪里出了問題。
因此我們還可以在第[6]之后加入:
// [7] 重定向輸入輸出 global $stdin, $stdout, $stderr; $stdin = fopen('/dev/null', 'r'); $stdout = fopen('/dev/null', 'wb'); // 你也可以將標準輸出重定向到指定的文件,相當于是日志 $stderr = fopen('/dev/null', 'wb'); // 同上
以上就是“PHP怎么實現守護進程”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。