php萬字碼出完美守護(hù)進(jìn)程詳解
前事提要
上期我們詳細(xì)學(xué)習(xí)了會話的概念以及用法,會話,進(jìn)程組,終端的理解對本篇講述的守護(hù)進(jìn)程極其重要,如還不理解相關(guān)概念建議翻看我往期關(guān)于會話,進(jìn)程組,終端文章。
基本概念
守護(hù)進(jìn)程(Daemon Process),也就是通常說的 Daemon 進(jìn)程(精靈進(jìn)程),是 Linux 中的后臺服務(wù)進(jìn)程。通常獨立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。并且不跟任何的控制終端關(guān)聯(lián),如果想讓某個進(jìn)程不因為用戶或中斷或其他變化而影響,那么就必須把這個進(jìn)程變成一個守護(hù)進(jìn)程。
常見的守護(hù)進(jìn)程包括系統(tǒng)日志進(jìn)程syslogd、 web服務(wù)器httpd、任務(wù)規(guī)劃守護(hù)進(jìn)程crond,數(shù)據(jù)庫服務(wù)器mysqld等。一般采用以 d 結(jié)尾的名字。
查看系統(tǒng)守護(hù)進(jìn)程命令 ps -efj
基本特點
生存周期長[非必須],一般操作系統(tǒng)啟動的時候就啟動,關(guān)閉的時候關(guān)閉。
守護(hù)進(jìn)程和終端無關(guān)聯(lián),也就是他們沒有控制終端,所以當(dāng)控制終端退出,也不會導(dǎo)致守護(hù)進(jìn)程退出。
守護(hù)進(jìn)程是在后臺運(yùn)行,不會占著終端,終端可以執(zhí)行其他命令
守護(hù)進(jìn)程的父進(jìn)程是1號進(jìn)程,也就是init進(jìn)程;
- 在Linux中 , 大概有三種方式實現(xiàn)腳本后臺化 :
1 . 在命令后添加一個&符號 , 比如 php task.php & . 這個方法的缺點在于 如果terminal終端關(guān)閉 , 無論是正常關(guān)閉還是非正常關(guān)閉 , 這個php進(jìn)程都會隨著終端關(guān)閉而關(guān)閉 , 其次是代碼中如果有echo或者print_r之類的輸出文本 , 會被輸出到當(dāng)前的終端窗口中 .
2 . 使用nohup命令 , 比如 nohup php task.php & . 默認(rèn)情況下 , 代碼中echo或者print_r之類輸出的文本會被輸出到php代碼同級目錄的nohup.out文件中 . 如果你用exit命令或者關(guān)閉按鈕等正常手段關(guān)閉終端 , 該進(jìn)程不會被關(guān)閉 , 依然會在后臺持續(xù)運(yùn)行 . 但是如果終端遇到異常退出或者終止 , 該php進(jìn)程也會隨即退出 . 本質(zhì)上 , 也并非穩(wěn)定可靠的daemon方案 .
3 . 使用fork和setsid , 我暫且稱之為 : *nix解決方案
創(chuàng)建守護(hù)進(jìn)程要求
- 1. 設(shè)置文件創(chuàng)建屏蔽字
umask(0)
文件創(chuàng)建屏蔽字是指屏蔽掉文件創(chuàng)建時的對應(yīng)位(umask() 控制系統(tǒng)文件和目錄默認(rèn)權(quán)限
)。由于使用fork系統(tǒng)調(diào)用新建的子進(jìn)程繼承了父進(jìn)程的文件創(chuàng)建掩碼,這就給該子進(jìn)程使用文件帶來了諸多的不便。因此,把文件創(chuàng)建掩碼設(shè)置為0,可以大大增強(qiáng)該守護(hù)進(jìn)程的靈活性。
- 2. 調(diào)用fork,父進(jìn)程退出(exit);
如果該守護(hù)進(jìn)程是作為一條簡單的shell命令啟動的,那么父進(jìn)程終止使得shell認(rèn)為該命令已經(jīng)執(zhí)行完畢;保證子進(jìn)程不是一個進(jìn)程組的組長進(jìn)程,為什么要保證不是進(jìn)程組組長呢? 因為進(jìn)程組組長調(diào)用setsid創(chuàng)建會話會報錯;
- 3. 子進(jìn)程調(diào)用setsid 函數(shù)來創(chuàng)建會話
先介紹一下Linux中的進(jìn)程與控制終端,登錄會話和進(jìn)程組之間的關(guān)系:進(jìn)程屬于一個進(jìn)程組,進(jìn)程組號(GID)就是進(jìn)程組長的進(jìn)程號(PID)。登錄會話可以包含多個進(jìn)程組。這些進(jìn)程組共享一個控制終端。這個控制終端通常是創(chuàng)建進(jìn)程的登錄終端。
控制終端,登錄會話和進(jìn)程組通常是從父進(jìn)程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第2點的基礎(chǔ)上,調(diào)用setsid()使進(jìn)程成為會話組長:
setsid()調(diào)用成功后,進(jìn)程成為新的會話組長和新的進(jìn)程組長,并與原來的登錄會話和進(jìn)程組脫離。由于會話過程對控制終端的獨占性,進(jìn)程同時與控制終端脫離。
調(diào)用setsid有3個作用:
讓進(jìn)程擺脫原會話的控制;
讓進(jìn)程擺脫原進(jìn)程組的控制;
讓進(jìn)程擺脫原控制終端的控制
- 4. 把守護(hù)進(jìn)程工作目錄設(shè)置為根目錄 chdir(“/”);
從父進(jìn)程繼承過來的工作目錄可能在一個掛載的文件系統(tǒng)中。由于守護(hù)進(jìn)程通常在系統(tǒng)再引導(dǎo)之前是一直存在的,所以如果守護(hù)進(jìn)程的當(dāng)前工作目錄在一個掛載的文件系統(tǒng)中,會導(dǎo)致該文件系統(tǒng)不能被卸載。
- 5.把一些文件描述符關(guān)閉 【標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯誤】
文件描述符:用來標(biāo)識一個文件。當(dāng)你打開一個存在的文件或者創(chuàng)建一個新文件,操作系統(tǒng)都會返回這個文件描述符。后續(xù)對這個文件的操作的一些函數(shù),都會用到這個文件描述符作為參數(shù)。
Linux中三個特殊的文件描述符,數(shù)字分別為0,1,2:
0:標(biāo)準(zhǔn)輸入[鍵盤],對應(yīng)的符號常量叫 STDIN_FILENO
1:標(biāo)準(zhǔn)輸出[屏幕],對應(yīng)的符號常量叫 STDOUT_FILENO
2:標(biāo)準(zhǔn)錯誤[屏幕],對應(yīng)的符號常量叫STDERR_FILENO
進(jìn)程從創(chuàng)建它的父進(jìn)程那里繼承了打開的文件描述符。如不關(guān)閉,將會浪費系統(tǒng)資源,造成進(jìn)程所在的文件系統(tǒng)無法卸下以及引起無法預(yù)料的錯誤。
- 6. 當(dāng)調(diào)用setsid函數(shù)后,一般會在創(chuàng)建一個子進(jìn)程,讓會話首進(jìn)程退出,確保該進(jìn)程不會再獲得控制終端
(1)調(diào)用一次fork的作用:
第一次fork的作用是讓shell認(rèn)為這條命令已經(jīng)終止,不用掛在終端輸入上,還有就是為了后面的setsid服務(wù),因為調(diào)用setsid函數(shù)的進(jìn)程不能是進(jìn)程組組長,如果不fork出子進(jìn)程,則此時的父進(jìn)程是進(jìn)程組組長,就無法調(diào)用setsid。當(dāng)子進(jìn)程調(diào)用完setsid函數(shù)之后,子進(jìn)程是會話組長也是進(jìn)程組組長,并且脫離了控制終端,此時,不管控制終端如何操作,新的進(jìn)程都不會收到一些信號使得進(jìn)程退出。
(2)第二次fork的作用:
雖然當(dāng)前關(guān)閉了和終端的聯(lián)系,但是后期可能會誤操作打開了終端。
只有會話首進(jìn)程能打開終端設(shè)備, 也就是再fork一次,再把父進(jìn)程退出,再次fork的子進(jìn)程作為守護(hù)進(jìn)程繼續(xù)運(yùn)行,保證了該守護(hù)進(jìn)程不再是會話的首進(jìn)程。
第二次不是必須的,是可選的。
- 7.編寫一個守護(hù)進(jìn)程
<?php // 1. 設(shè)置文件創(chuàng)建屏蔽字 umask(0); // 2. fork 子進(jìn)程 $pid = pcntl_fork(); if($pid > 0){ print("父進(jìn)程退出\n"); exit(0); } //3. 設(shè)置當(dāng)前子進(jìn)程為會話首進(jìn)程,進(jìn)程組長,斷開與終端的連接,成為后臺進(jìn)程 if(-1 === posix_setsid()){ print("sid err \n"); } // 4. 把守護(hù)進(jìn)程工作目錄設(shè)置為根目錄 chdir("/"); //已經(jīng)成為守護(hù)進(jìn)程~\(≧▽≦)/~啦 while(1){ echo "test".PHP_EOL; sleep(2); }
將文件保存為daemon.php,然后php daemon.php執(zhí)行文件,嗯,執(zhí)行結(jié)果卻有些奇怪,大概類似于下圖:
即便你按Ctrl+C
都沒用,終端在不斷輸出test,唯一辦法就是關(guān)閉當(dāng)前終端窗口然后重新開一個,為什么會這樣,這就涉及到我們上面提到的第5點,需要關(guān)閉繼承過來的標(biāo)準(zhǔn)輸出,輸入,錯誤,這樣我們的daemon程序不可以再將終端窗口當(dāng)作默認(rèn)的標(biāo)準(zhǔn)輸出了。
<?php // 設(shè)置文件創(chuàng)建屏蔽字 umask(0); // 第一次fork 子進(jìn)程 $pid = pcntl_fork(); if($pid > 0){ print("父進(jìn)程退出\n"); exit(0); } //設(shè)置當(dāng)前子進(jìn)程為會話首進(jìn)程,進(jìn)程組長,斷開與終端的連接,成為后臺進(jìn)程 if(-1 === posix_setsid()){ print("sid err \n"); } //第二次fork 徹底斷開控制終端 $pid = pcntl_fork(); if($pid > 0){ exit(0);//讓會話首進(jìn)程退出 } // 把守護(hù)進(jìn)程工作目錄設(shè)置為根目錄 chdir("/"); // 關(guān)閉標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯誤,linux 中使用數(shù)字表示文件描述符也就是 0,1,2 fclose(STDIN);//0 fclose(STDOUT);//1 fclose(STDERR);//2 //當(dāng)關(guān)掉以上標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)錯誤之后,如果后面要對文件操作(比如打開一個文件,寫入,創(chuàng)建)它返回的文件描述符從0開始,這樣可能造成未知異常 //為了避免問題,我們使用輸出從定向到 /dev/null 空設(shè)備文件解決這個問題,重新設(shè)置0,1,2 文件描述符用來代替標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯誤,往 /dev/null 寫入數(shù)據(jù)會被丟棄,這樣就不會向終端輸出數(shù)據(jù)了。 $stdin = fopen("/dev/null",'a'); $stdout = fopen("/dev/null",'a'); $stderr = fopen("/dev/null", 'a'); //已經(jīng)成為守護(hù)進(jìn)程~\(≧▽≦)/~啦 while(1){ echo "test".PHP_EOL; sleep(2); }
空設(shè)備
/dev/null : 是一個特殊的設(shè)備文件,它丟棄一切寫入其中的數(shù)據(jù)(像黑洞一些)例如:echo “大雷編程” > /dev/null 輸出重定向文件到黑洞(無任何輸出)。
我們一般把守護(hù)進(jìn)程的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出重定向到空設(shè)備(黑洞),從而確保守護(hù)進(jìn)程不從鍵盤接收任何東西,也不把輸出結(jié)果打印到屏幕。
到此這篇關(guān)于php萬字碼出完美守護(hù)進(jìn)程詳解的文章就介紹到這了,更多相關(guān)php守護(hù)進(jìn)程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
fleaphp crud操作之findByField函數(shù)的使用方法
fleaphp crud操作之findByField函數(shù)的用法分享,需要的朋友可以參考下。2011-04-04php自定義urlencode,urldecode函數(shù)實例
這篇文章主要介紹了php自定義urlencode,urldecode函數(shù),實例分析了php字符串轉(zhuǎn)碼的技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-03-03使用PHP連接多種數(shù)據(jù)庫的實現(xiàn)代碼(mysql,access,sqlserver,Oracle)
我們今天為大家介紹的PHP連接數(shù)據(jù)庫的方法包括在MYSQL數(shù)據(jù)庫、ACCESS數(shù)據(jù)庫、MS SQL數(shù)據(jù)庫和Oracle數(shù)據(jù)庫中實現(xiàn)2016-12-12淺析php與數(shù)據(jù)庫代碼開發(fā)規(guī)范
以下是對php與數(shù)據(jù)庫代碼開發(fā)規(guī)范進(jìn)行了簡單的分析介紹。需要的朋友可以過來參考下2013-08-08