您好,登錄后才能下訂單哦!
這篇“怎么使用php編寫守護進程”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“怎么使用php編寫守護進程”文章吧。
守護進程(Daemon Process),也就是通常說的 Daemon 進程(精靈進程),是 Linux 中的后臺服務進程。通常獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件。并且不跟任何的控制終端關聯,如果想讓某個進程不因為用戶或中斷或其他變化而影響,那么就必須把這個進程變成一個守護進程。
常見的守護進程包括系統日志進程syslogd、 web服務器httpd、任務規劃守護進程crond,數據庫服務器mysqld等。一般采用以 d 結尾的名字。
查看系統守護進程命令 ps -efj
生存周期長[非必須],一般操作系統啟動的時候就啟動,關閉的時候關閉。
守護進程和終端無關聯,也就是他們沒有控制終端,所以當控制終端退出,也不會導致守護進程退出。
守護進程是在后臺運行,不會占著終端,終端可以執行其他命令
守護進程的父進程是1號進程,也就是init進程;
在Linux中 , 大概有三種方式實現腳本后臺化 :
1 . 在命令后添加一個&符號 , 比如 php task.php & . 這個方法的缺點在于 如果terminal終端關閉 , 無論是正常關閉還是非正常關閉 , 這個php進程都會隨著終端關閉而關閉 , 其次是代碼中如果有echo或者print_r之類的輸出文本 , 會被輸出到當前的終端窗口中 .
2 . 使用nohup命令 , 比如 nohup php task.php & . 默認情況下 , 代碼中echo或者print_r之類輸出的文本會被輸出到php代碼同級目錄的nohup.out文件中 . 如果你用exit命令或者關閉按鈕等正常手段關閉終端 , 該進程不會被關閉 , 依然會在后臺持續運行 . 但是如果終端遇到異常退出或者終止 , 該php進程也會隨即退出 . 本質上 , 也并非穩定可靠的daemon方案 .
3 . 使用fork和setsid , 我暫且稱之為 : *nix解決方案
1. 設置文件創建屏蔽字 umask(0)
文件創建屏蔽字是指屏蔽掉文件創建時的對應位(umask() 控制系統文件和目錄默認權限
)。由于使用fork系統調用新建的子進程繼承了父進程的文件創建掩碼,這就給該子進程使用文件帶來了諸多的不便。因此,把文件創建掩碼設置為0,可以大大增強該守護進程的靈活性。
2. 調用fork,父進程退出(exit);
如果該守護進程是作為一條簡單的shell命令啟動的,那么父進程終止使得shell認為該命令已經執行完畢;保證子進程不是一個進程組的組長進程,為什么要保證不是進程組組長呢? 因為進程組組長調用setsid創建會話會報錯;
3. 子進程調用setsid 函數來創建會話
先介紹一下Linux中的進程與控制終端,登錄會話和進程組之間的關系:進程屬于一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登錄會話可以包含多個進程組。這些進程組共享一個控制終端。這個控制終端通常是創建進程的登錄終端。
控制終端,登錄會話和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第2點的基礎上,調用setsid()使進程成為會話組長:
setsid()調用成功后,進程成為新的會話組長和新的進程組長,并與原來的登錄會話和進程組脫離。由于會話過程對控制終端的獨占性,進程同時與控制終端脫離。
調用setsid有3個作用:
讓進程擺脫原會話的控制;
讓進程擺脫原進程組的控制;
讓進程擺脫原控制終端的控制
4. 把守護進程工作目錄設置為根目錄 chdir(“/”);
從父進程繼承過來的工作目錄可能在一個掛載的文件系統中。由于守護進程通常在系統再引導之前是一直存在的,所以如果守護進程的當前工作目錄在一個掛載的文件系統中,會導致該文件系統不能被卸載。
5.把一些文件描述符關閉 【標準輸入,標準輸出,標準錯誤】
文件描述符:用來標識一個文件。當你打開一個存在的文件或者創建一個新文件,操作系統都會返回這個文件描述符。后續對這個文件的操作的一些函數,都會用到這個文件描述符作為參數。
Linux中三個特殊的文件描述符,數字分別為0,1,2:
0:標準輸入[鍵盤],對應的符號常量叫 STDIN_FILENO
1:標準輸出[屏幕],對應的符號常量叫 STDOUT_FILENO
2:標準錯誤[屏幕],對應的符號常量叫STDERR_FILENO
進程從創建它的父進程那里繼承了打開的文件描述符。如不關閉,將會浪費系統資源,造成進程所在的文件系統無法卸下以及引起無法預料的錯誤。
6. 當調用setsid函數后,一般會在創建一個子進程,讓會話首進程退出,確保該進程不會再獲得控制終端
(1)調用一次fork的作用:
第一次fork的作用是讓shell認為這條命令已經終止,不用掛在終端輸入上,還有就是為了后面的setsid服務,因為調用setsid函數的進程不能是進程組組長,如果不fork出子進程,則此時的父進程是進程組組長,就無法調用setsid。當子進程調用完setsid函數之后,子進程是會話組長也是進程組組長,并且脫離了控制終端,此時,不管控制終端如何操作,新的進程都不會收到一些信號使得進程退出。
(2)第二次fork的作用:
雖然當前關閉了和終端的聯系,但是后期可能會誤操作打開了終端。
只有會話首進程能打開終端設備, 也就是再fork一次,再把父進程退出,再次fork的子進程作為守護進程繼續運行,保證了該守護進程不再是會話的首進程。
第二次不是必須的,是可選的。
7.編寫一個守護進程
<?php // 1. 設置文件創建屏蔽字 umask(0); // 2. fork 子進程 $pid = pcntl_fork(); if($pid > 0){ print("父進程退出\n"); exit(0); } //3. 設置當前子進程為會話首進程,進程組長,斷開與終端的連接,成為后臺進程 if(-1 === posix_setsid()){ print("sid err \n"); } // 4. 把守護進程工作目錄設置為根目錄 chdir("/"); //已經成為守護進程~\(≧▽≦)/~啦 while(1){ echo "test".PHP_EOL; sleep(2); }
將文件保存為daemon.php,然后php daemon.php執行文件,嗯,執行結果卻有些奇怪,大概類似于下圖:
即便你按Ctrl+C
都沒用,終端在不斷輸出test,唯一辦法就是關閉當前終端窗口然后重新開一個,為什么會這樣,這就涉及到我們上面提到的第5點,需要關閉繼承過來的標準輸出,輸入,錯誤,這樣我們的daemon程序不可以再將終端窗口當作默認的標準輸出了。
<?php // 設置文件創建屏蔽字 umask(0); // 第一次fork 子進程 $pid = pcntl_fork(); if($pid > 0){ print("父進程退出\n"); exit(0); } //設置當前子進程為會話首進程,進程組長,斷開與終端的連接,成為后臺進程 if(-1 === posix_setsid()){ print("sid err \n"); } //第二次fork 徹底斷開控制終端 $pid = pcntl_fork(); if($pid > 0){ exit(0);//讓會話首進程退出 } // 把守護進程工作目錄設置為根目錄 chdir("/"); // 關閉標準輸入,標準輸出,標準錯誤,linux 中使用數字表示文件描述符也就是 0,1,2 fclose(STDIN);//0 fclose(STDOUT);//1 fclose(STDERR);//2 //當關掉以上標準輸出,標準輸入,標準錯誤之后,如果后面要對文件操作(比如打開一個文件,寫入,創建)它返回的文件描述符從0開始,這樣可能造成未知異常 //為了避免問題,我們使用輸出從定向到 /dev/null 空設備文件解決這個問題,重新設置0,1,2 文件描述符用來代替標準輸入,標準輸出,標準錯誤,往 /dev/null 寫入數據會被丟棄,這樣就不會向終端輸出數據了。 $stdin = fopen("/dev/null",'a'); $stdout = fopen("/dev/null",'a'); $stderr = fopen("/dev/null", 'a'); //已經成為守護進程~\(≧▽≦)/~啦 while(1){ echo "test".PHP_EOL; sleep(2); }
空設備
/dev/null : 是一個特殊的設備文件,它丟棄一切寫入其中的數據(像黑洞一些)例如:echo “大雷編程” > /dev/null 輸出重定向文件到黑洞(無任何輸出)。
我們一般把守護進程的標準輸入、標準輸出重定向到空設備(黑洞),從而確保守護進程不從鍵盤接收任何東西,也不把輸出結果打印到屏幕。
以上就是關于“怎么使用php編寫守護進程”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。