欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

php萬(wàn)字碼出完美守護(hù)進(jìn)程詳解

 更新時(shí)間:2022年07月27日 08:54:47   作者:大雷編程  
守護(hù)進(jìn)程到底是怎么實(shí)現(xiàn)的?為什么有的程序既可以自己就成為守護(hù)進(jìn)程,又可以通過(guò)systemd 來(lái)后臺(tái)運(yùn)行?本文將為大家具體講解,感興趣的可以了解一下

前事提要

上期我們?cè)敿?xì)學(xué)習(xí)了會(huì)話的概念以及用法,會(huì)話,進(jìn)程組,終端的理解對(duì)本篇講述的守護(hù)進(jìn)程極其重要,如還不理解相關(guān)概念建議翻看我往期關(guān)于會(huì)話,進(jìn)程組,終端文章。

基本概念

守護(hù)進(jìn)程(Daemon Process),也就是通常說(shuō)的 Daemon 進(jìn)程(精靈進(jìn)程),是 Linux 中的后臺(tái)服務(wù)進(jìn)程。通常獨(dú)立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。并且不跟任何的控制終端關(guān)聯(lián),如果想讓某個(gè)進(jìn)程不因?yàn)橛脩艋蛑袛嗷蚱渌兓绊?,那么就必須把這個(gè)進(jìn)程變成一個(gè)守護(hù)進(jìn)程。

常見(jiàn)的守護(hù)進(jìn)程包括系統(tǒng)日志進(jìn)程syslogd、 web服務(wù)器httpd、任務(wù)規(guī)劃守護(hù)進(jìn)程crond,數(shù)據(jù)庫(kù)服務(wù)器mysqld等。一般采用以 d 結(jié)尾的名字。

查看系統(tǒng)守護(hù)進(jìn)程命令 ps -efj

基本特點(diǎn)

生存周期長(zhǎng)[非必須],一般操作系統(tǒng)啟動(dòng)的時(shí)候就啟動(dòng),關(guān)閉的時(shí)候關(guān)閉。

守護(hù)進(jìn)程和終端無(wú)關(guān)聯(lián),也就是他們沒(méi)有控制終端,所以當(dāng)控制終端退出,也不會(huì)導(dǎo)致守護(hù)進(jìn)程退出。

守護(hù)進(jìn)程是在后臺(tái)運(yùn)行,不會(huì)占著終端,終端可以執(zhí)行其他命令

守護(hù)進(jìn)程的父進(jìn)程是1號(hào)進(jìn)程,也就是init進(jìn)程;

  • 在Linux中 , 大概有三種方式實(shí)現(xiàn)腳本后臺(tái)化 :

1 . 在命令后添加一個(gè)&符號(hào) , 比如 php task.php & . 這個(gè)方法的缺點(diǎn)在于 如果terminal終端關(guān)閉 , 無(wú)論是正常關(guān)閉還是非正常關(guān)閉 , 這個(gè)php進(jìn)程都會(huì)隨著終端關(guān)閉而關(guān)閉 , 其次是代碼中如果有echo或者print_r之類(lèi)的輸出文本 , 會(huì)被輸出到當(dāng)前的終端窗口中 .

2 . 使用nohup命令 , 比如 nohup php task.php & . 默認(rèn)情況下 , 代碼中echo或者print_r之類(lèi)輸出的文本會(huì)被輸出到php代碼同級(jí)目錄的nohup.out文件中 . 如果你用exit命令或者關(guān)閉按鈕等正常手段關(guān)閉終端 , 該進(jìn)程不會(huì)被關(guān)閉 , 依然會(huì)在后臺(tái)持續(xù)運(yùn)行 . 但是如果終端遇到異常退出或者終止 , 該php進(jìn)程也會(huì)隨即退出 . 本質(zhì)上 , 也并非穩(wěn)定可靠的daemon方案 .

3 . 使用fork和setsid , 我暫且稱(chēng)之為 : *nix解決方案

創(chuàng)建守護(hù)進(jìn)程要求

  • 1. 設(shè)置文件創(chuàng)建屏蔽字 umask(0)

文件創(chuàng)建屏蔽字是指屏蔽掉文件創(chuàng)建時(shí)的對(duì)應(yīng)位(umask() 控制系統(tǒng)文件和目錄默認(rèn)權(quán)限)。由于使用fork系統(tǒng)調(diào)用新建的子進(jìn)程繼承了父進(jìn)程的文件創(chuàng)建掩碼,這就給該子進(jìn)程使用文件帶來(lái)了諸多的不便。因此,把文件創(chuàng)建掩碼設(shè)置為0,可以大大增強(qiáng)該守護(hù)進(jìn)程的靈活性。

  • 2. 調(diào)用fork,父進(jìn)程退出(exit);

如果該守護(hù)進(jìn)程是作為一條簡(jiǎn)單的shell命令啟動(dòng)的,那么父進(jìn)程終止使得shell認(rèn)為該命令已經(jīng)執(zhí)行完畢;保證子進(jìn)程不是一個(gè)進(jìn)程組的組長(zhǎng)進(jìn)程,為什么要保證不是進(jìn)程組組長(zhǎng)呢? 因?yàn)檫M(jìn)程組組長(zhǎng)調(diào)用setsid創(chuàng)建會(huì)話會(huì)報(bào)錯(cuò);

  • 3. 子進(jìn)程調(diào)用setsid 函數(shù)來(lái)創(chuàng)建會(huì)話

先介紹一下Linux中的進(jìn)程與控制終端,登錄會(huì)話和進(jìn)程組之間的關(guān)系:進(jìn)程屬于一個(gè)進(jìn)程組,進(jìn)程組號(hào)(GID)就是進(jìn)程組長(zhǎng)的進(jìn)程號(hào)(PID)。登錄會(huì)話可以包含多個(gè)進(jìn)程組。這些進(jìn)程組共享一個(gè)控制終端。這個(gè)控制終端通常是創(chuàng)建進(jìn)程的登錄終端。

控制終端,登錄會(huì)話和進(jìn)程組通常是從父進(jìn)程繼承下來(lái)的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第2點(diǎn)的基礎(chǔ)上,調(diào)用setsid()使進(jìn)程成為會(huì)話組長(zhǎng):

setsid()調(diào)用成功后,進(jìn)程成為新的會(huì)話組長(zhǎng)和新的進(jìn)程組長(zhǎng),并與原來(lái)的登錄會(huì)話和進(jìn)程組脫離。由于會(huì)話過(guò)程對(duì)控制終端的獨(dú)占性,進(jìn)程同時(shí)與控制終端脫離。

調(diào)用setsid有3個(gè)作用:

讓進(jìn)程擺脫原會(huì)話的控制;

讓進(jìn)程擺脫原進(jìn)程組的控制;

讓進(jìn)程擺脫原控制終端的控制

  • 4. 把守護(hù)進(jìn)程工作目錄設(shè)置為根目錄 chdir(“/”);

從父進(jìn)程繼承過(guò)來(lái)的工作目錄可能在一個(gè)掛載的文件系統(tǒng)中。由于守護(hù)進(jìn)程通常在系統(tǒng)再引導(dǎo)之前是一直存在的,所以如果守護(hù)進(jìn)程的當(dāng)前工作目錄在一個(gè)掛載的文件系統(tǒng)中,會(huì)導(dǎo)致該文件系統(tǒng)不能被卸載。

  • 5.把一些文件描述符關(guān)閉 【標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯(cuò)誤】

文件描述符:用來(lái)標(biāo)識(shí)一個(gè)文件。當(dāng)你打開(kāi)一個(gè)存在的文件或者創(chuàng)建一個(gè)新文件,操作系統(tǒng)都會(huì)返回這個(gè)文件描述符。后續(xù)對(duì)這個(gè)文件的操作的一些函數(shù),都會(huì)用到這個(gè)文件描述符作為參數(shù)。

Linux中三個(gè)特殊的文件描述符,數(shù)字分別為0,1,2:

0:標(biāo)準(zhǔn)輸入[鍵盤(pán)],對(duì)應(yīng)的符號(hào)常量叫 STDIN_FILENO

1:標(biāo)準(zhǔn)輸出[屏幕],對(duì)應(yīng)的符號(hào)常量叫 STDOUT_FILENO

2:標(biāo)準(zhǔn)錯(cuò)誤[屏幕],對(duì)應(yīng)的符號(hào)常量叫STDERR_FILENO

進(jìn)程從創(chuàng)建它的父進(jìn)程那里繼承了打開(kāi)的文件描述符。如不關(guān)閉,將會(huì)浪費(fèi)系統(tǒng)資源,造成進(jìn)程所在的文件系統(tǒng)無(wú)法卸下以及引起無(wú)法預(yù)料的錯(cuò)誤。

  • 6. 當(dāng)調(diào)用setsid函數(shù)后,一般會(huì)在創(chuàng)建一個(gè)子進(jìn)程,讓會(huì)話首進(jìn)程退出,確保該進(jìn)程不會(huì)再獲得控制終端

(1)調(diào)用一次fork的作用:

第一次fork的作用是讓shell認(rèn)為這條命令已經(jīng)終止,不用掛在終端輸入上,還有就是為了后面的setsid服務(wù),因?yàn)檎{(diào)用setsid函數(shù)的進(jìn)程不能是進(jìn)程組組長(zhǎng),如果不fork出子進(jìn)程,則此時(shí)的父進(jìn)程是進(jìn)程組組長(zhǎng),就無(wú)法調(diào)用setsid。當(dāng)子進(jìn)程調(diào)用完setsid函數(shù)之后,子進(jìn)程是會(huì)話組長(zhǎng)也是進(jìn)程組組長(zhǎng),并且脫離了控制終端,此時(shí),不管控制終端如何操作,新的進(jìn)程都不會(huì)收到一些信號(hào)使得進(jìn)程退出。

(2)第二次fork的作用:

雖然當(dāng)前關(guān)閉了和終端的聯(lián)系,但是后期可能會(huì)誤操作打開(kāi)了終端。

只有會(huì)話首進(jìn)程能打開(kāi)終端設(shè)備, 也就是再fork一次,再把父進(jìn)程退出,再次fork的子進(jìn)程作為守護(hù)進(jìn)程繼續(xù)運(yùn)行,保證了該守護(hù)進(jìn)程不再是會(huì)話的首進(jìn)程。

第二次不是必須的,是可選的。

  • 7.編寫(xiě)一個(gè)守護(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)程為會(huì)話首進(jìn)程,進(jìn)程組長(zhǎng),斷開(kāi)與終端的連接,成為后臺(tái)進(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é)果卻有些奇怪,大概類(lèi)似于下圖:

即便你按Ctrl+C都沒(méi)用,終端在不斷輸出test,唯一辦法就是關(guān)閉當(dāng)前終端窗口然后重新開(kāi)一個(gè),為什么會(huì)這樣,這就涉及到我們上面提到的第5點(diǎn),需要關(guān)閉繼承過(guò)來(lái)的標(biāo)準(zhǔn)輸出,輸入,錯(cuò)誤,這樣我們的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)程為會(huì)話首進(jìn)程,進(jìn)程組長(zhǎng),斷開(kāi)與終端的連接,成為后臺(tái)進(jìn)程
if(-1 === posix_setsid()){
        print("sid err \n");
}
//第二次fork 徹底斷開(kāi)控制終端
$pid = pcntl_fork();
if($pid > 0){
	exit(0);//讓會(huì)話首進(jìn)程退出
}
// 把守護(hù)進(jìn)程工作目錄設(shè)置為根目錄
chdir("/");
// 關(guān)閉標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯(cuò)誤,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)錯(cuò)誤之后,如果后面要對(duì)文件操作(比如打開(kāi)一個(gè)文件,寫(xiě)入,創(chuàng)建)它返回的文件描述符從0開(kāi)始,這樣可能造成未知異常
//為了避免問(wèn)題,我們使用輸出從定向到 /dev/null 空設(shè)備文件解決這個(gè)問(wèn)題,重新設(shè)置0,1,2 文件描述符用來(lái)代替標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯(cuò)誤,往 /dev/null 寫(xiě)入數(shù)據(jù)會(huì)被丟棄,這樣就不會(huì)向終端輸出數(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 : 是一個(gè)特殊的設(shè)備文件,它丟棄一切寫(xiě)入其中的數(shù)據(jù)(像黑洞一些)例如:echo “大雷編程” > /dev/null 輸出重定向文件到黑洞(無(wú)任何輸出)。

我們一般把守護(hù)進(jìn)程的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出重定向到空設(shè)備(黑洞),從而確保守護(hù)進(jìn)程不從鍵盤(pán)接收任何東西,也不把輸出結(jié)果打印到屏幕。

到此這篇關(guān)于php萬(wàn)字碼出完美守護(hù)進(jìn)程詳解的文章就介紹到這了,更多相關(guān)php守護(hù)進(jìn)程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論