摘要:因?yàn)樽舆M(jìn)程一定不會(huì)是組長進(jìn)程,所以子進(jìn)程可以調(diào)用。主進(jìn)程退出子進(jìn)程繼續(xù)執(zhí)行啦啦啦,啦啦啦,啦啦啦,已經(jīng)變成啦,開心一般服務(wù)器軟件都有寫配置項(xiàng),比如以模式運(yùn)行還是以模式運(yùn)行。
[原文地址:https://blog.ti-node.com/blog...]
其實(shí)前面是談過一次daemon進(jìn)程的,但是并涉及過多原理,但是并不影響使用。今天打算說說關(guān)于daemon進(jìn)程更多的二三事,本質(zhì)上說,如果你僅僅是簡(jiǎn)單實(shí)現(xiàn)利用一下daemon進(jìn)程,這個(gè)不看也是可以的。
杠真,*NIX真是波大精深,越是深入看越是發(fā)現(xiàn)它的diao。原理往往都是枯燥的,大家都不愛看,但這并不影響我堅(jiān)持寫自己對(duì)這些東西的理解。
三個(gè)概念,理(bei)解(song)一下:
進(jìn)程組。一坨相關(guān)的進(jìn)程可以組成一個(gè)進(jìn)程組,每個(gè)進(jìn)程組都會(huì)有一個(gè)組ID(正整數(shù)),每個(gè)進(jìn)程組都會(huì)有一個(gè)組長進(jìn)程,組長進(jìn)程的ID等于進(jìn)程組ID。組長進(jìn)程可以創(chuàng)建新的進(jìn)程組以及該進(jìn)程組中的其他進(jìn)程。一個(gè)進(jìn)程組的是有生命周期的,即便是組長進(jìn)程掛了,只有組里還有其他的活口,那就就算該進(jìn)程組依然存活,只有到組里最后一個(gè)活口也掛了,那真的就是徹底沒了。
會(huì)話。一坨相關(guān)的進(jìn)程組組成了一個(gè)會(huì)話。在*NIX下,是通過setsid()創(chuàng)建一個(gè)新的會(huì)話。但是值得注意的是,組長進(jìn)程不能創(chuàng)建會(huì)話,簡(jiǎn)單理解就是在組長進(jìn)程中,執(zhí)行setsid函數(shù)會(huì)報(bào)錯(cuò),這點(diǎn)很重要。所以一般都是組長進(jìn)程執(zhí)行fork,然后主進(jìn)程退出,因?yàn)樽舆M(jìn)程的進(jìn)程ID是新分配的,而子進(jìn)程的進(jìn)程組ID是繼承父進(jìn)程的,所以子進(jìn)程就注定不可能是組長進(jìn)程,從而可以確保子進(jìn)程中一定可以執(zhí)行setsid函數(shù)。在執(zhí)行setsid函數(shù)時(shí)候,一般會(huì)發(fā)生下面三個(gè)比較重要的事情:
該進(jìn)程會(huì)創(chuàng)建一個(gè)新的進(jìn)程組,該進(jìn)程為進(jìn)程組組長(或者你可以認(rèn)為這是一種提升)
該進(jìn)程會(huì)創(chuàng)建一個(gè)會(huì)話組并成為該會(huì)話的會(huì)話首進(jìn)程(會(huì)話首進(jìn)程就是創(chuàng)建該會(huì)話的進(jìn)程)
該進(jìn)程會(huì)失去控制終端。如果該進(jìn)程本來就沒有控制終端,則罷了(liao)。如果有,那么該進(jìn)程也將脫離該控制終端,與之失去聯(lián)系。
控制終端。每個(gè)會(huì)話可能會(huì)擁有一個(gè)控制終端(看著比較玄學(xué),你可以暫時(shí)理解為就一個(gè)那種黑乎乎的命令行窗口),建立與控制終端連接的會(huì)話首進(jìn)程叫做控制進(jìn)程。
結(jié)合Linux命令ps來查看一下上述幾個(gè)概念的恩怨情仇,我們看下我們常用的 ps -o pid,ppid,pgid,sid,comm | less 執(zhí)行結(jié)果:
第一行分別是PID,PPID,PGID,SID,COMMAND,依次分別是進(jìn)程ID,該進(jìn)程父進(jìn)程ID,進(jìn)程組ID,會(huì)話ID,命令。
通過最后一列,我們知道第二行就是bash也就是bash shell進(jìn)程,其進(jìn)程ID為15793,其父進(jìn)程為13291,進(jìn)程組ID為15793,會(huì)話ID也會(huì)15793,結(jié)合前面的概念,我們可以知道bash shell就是該進(jìn)程組組長。
第三行則是ps命令的進(jìn)程,其進(jìn)程ID為15816,他是由于bash進(jìn)程fork出來的,所以他的父進(jìn)程ID為15793,然后是他所屬的組ID為15816,所屬的會(huì)話ID依然是15793。
最后一行是less命令的進(jìn)程,其進(jìn)程ID為15817,他也是由bash進(jìn)程fork出來的,所以他的父進(jìn)程ID也為15793,然后是他所屬的組ID為15816,所屬的會(huì)話ID依然是15793。
簡(jiǎn)單總結(jié)一下:
上述三個(gè)進(jìn)程一共形成了兩個(gè)進(jìn)程組,bash自己為一組,組ID為15793,組長進(jìn)程為bash自己 ; ps和less為一組,組ID為15816,組長進(jìn)程為ps進(jìn)程
上述三個(gè)進(jìn)程屬于同一個(gè)會(huì)話,會(huì)話ID為15793,會(huì)話首進(jìn)程為bash進(jìn)程(待定)
控制終端則為打開的terminal窗口,與之關(guān)聯(lián)的控制進(jìn)程則為bash進(jìn)程
通過這么一頓分析,是不是感覺可以接受點(diǎn)兒了?然后是,叨逼叨了半天這個(gè),跟daemon進(jìn)程有啥子關(guān)系?
啦啦啦,下面通過引入代碼直接分析:
$pid = pcntl_fork(); if( $pid < 0 ){ exit("fork error."); } else if( $pid > 0 ) { // 主進(jìn)程退出 exit(); } // 子進(jìn)程繼續(xù)執(zhí)行 // 最關(guān)鍵的一步來了,執(zhí)行setsid函數(shù)! if( !posix_setsid() ){ exit("setsid error."); } // 理論上一次fork就可以了 // 但是,二次fork,這里的歷史淵源是這樣的:在基于system V的系統(tǒng)中,通過再次fork,父進(jìn)程退出,子進(jìn)程繼續(xù),保證形成的daemon進(jìn)程絕對(duì)不會(huì)成為會(huì)話首進(jìn)程,不會(huì)擁有控制終端。 $pid = pcntl_fork(); if( $pid < 0 ){ exit("fork error"); } else if( $pid > 0 ) { // 主進(jìn)程退出 exit; } // 子進(jìn)程繼續(xù)執(zhí)行 // 啦啦啦,啦啦啦,啦啦啦,已經(jīng)變成daemon啦,開心 cli_set_process_title("testtesttest"); // 睡眠1000000,防止進(jìn)程執(zhí)行完畢掛了 sleep( 1000000 );
將上述文件保存為daemon.php,然后php daemon.php執(zhí)行,使用 ps -aux | grep testte ,如果沒有什么大問題你應(yīng)該就可以看到這個(gè)進(jìn)程在后臺(tái)跑了。
所以為什么第一步要先fork呢?因?yàn)檎{(diào)用setsid的進(jìn)程不可以是組長進(jìn)程(篇頭的枯燥知識(shí)需要了吧?),所以必須fork一次,然后將主進(jìn)程直接退出,保留子進(jìn)程。因?yàn)樽舆M(jìn)程一定不會(huì)是組長進(jìn)程,所以子進(jìn)程可以調(diào)用setsid。調(diào)用setsid則會(huì)產(chǎn)生三個(gè)現(xiàn)象:創(chuàng)建一個(gè)新會(huì)話并成為會(huì)話首進(jìn)程,創(chuàng)建一個(gè)進(jìn)程組并成為組長進(jìn)程,脫離控制終端。
啦啦啦,明白為啥篇頭那一坨枯燥的知識(shí)是為了什么吧?
然而,實(shí)際上,上述代碼僅僅完成了一個(gè)標(biāo)準(zhǔn)daemon的80%,還有20%需要我們進(jìn)一步完善。那么,需要完善什么呢?我們修改一下上述代碼,讓程序在最終的代碼段中執(zhí)行一些文本輸出:
$pid = pcntl_fork(); if( $pid < 0 ){ exit("fork error."); } else if( $pid > 0 ) { // 主進(jìn)程退出 exit(); } // 子進(jìn)程繼續(xù)執(zhí)行 // 最關(guān)鍵的一步來了,執(zhí)行setsid函數(shù)! if( !posix_setsid() ){ exit("setsid error."); } // 理論上一次fork就可以了 // 但是,二次fork,這里的歷史淵源是這樣的:在基于system V的系統(tǒng)中,通過再次fork,父進(jìn)程退出,子進(jìn)程繼續(xù),保證形成的daemon進(jìn)程絕對(duì)不會(huì)成為會(huì)話首進(jìn)程,不會(huì)擁有控制終端。 $pid = pcntl_fork(); if( $pid < 0 ){ exit("fork error"); } else if( $pid > 0 ) { // 主進(jìn)程退出 exit; } // 子進(jìn)程繼續(xù)執(zhí)行 // 啦啦啦,啦啦啦,啦啦啦,已經(jīng)變成daemon啦,開心 cli_set_process_title("testtesttest"); // 循環(huán)1000次,每次睡眠1s,輸出一個(gè)字符test for( $i = 1; $i <= 1000; $i++ ){ sleep( 1 ); echo "test".PHP_EOL; }
將文件保存為daemon.php,然后php daemon.php執(zhí)行文件,嗯,是不是有怪怪的現(xiàn)象,大概類似于下圖:
即便你按Ctrl+C都沒用,終端在不斷輸出test,唯一辦法就是關(guān)閉當(dāng)前終端窗口然后重新開一個(gè),然而,這并不符合社會(huì)主義主流價(jià)值觀。所以,我們還要解決標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出,我們的daemon程序不可以再將終端窗口當(dāng)作默認(rèn)的標(biāo)準(zhǔn)輸出了。
其次是將當(dāng)前工作目錄修改更改為根目錄。不然可能就會(huì)出現(xiàn)下面這樣一個(gè)問題,就是如果父進(jìn)程是的工作目錄是一個(gè)掛載的目錄,那么子進(jìn)程會(huì)繼承父進(jìn)程的工作目錄,當(dāng)子進(jìn)程已經(jīng)daemon化后就會(huì)出現(xiàn)一個(gè)悲劇:那就是雖然原來掛載的目錄已經(jīng)不用了,但是卻無法用umount卸載,非常悲劇。
// 設(shè)置umask為0,這樣,當(dāng)前進(jìn)程創(chuàng)建的文件權(quán)限則為777 umask( 0 ); $pid = pcntl_fork(); if( $pid < 0 ){ exit("fork error."); } else if( $pid > 0 ) { // 主進(jìn)程退出 exit(); } // 子進(jìn)程繼續(xù)執(zhí)行 // 最關(guān)鍵的一步來了,執(zhí)行setsid函數(shù)! if( !posix_setsid() ){ exit("setsid error."); } // 理論上一次fork就可以了 // 但是,二次fork,這里的歷史淵源是這樣的:在基于system V的系統(tǒng)中,通過再次fork,父進(jìn)程退出,子進(jìn)程繼續(xù),保證形成的daemon進(jìn)程絕對(duì)不會(huì)成為會(huì)話首進(jìn)程,不會(huì)擁有控制終端。 $pid = pcntl_fork(); if( $pid < 0 ){ exit("fork error"); } else if( $pid > 0 ) { // 主進(jìn)程退出 exit; } // 子進(jìn)程繼續(xù)執(zhí)行 // 啦啦啦,啦啦啦,啦啦啦,已經(jīng)變成daemon啦,開心 cli_set_process_title("testtesttest"); // 一般服務(wù)器軟件都有寫配置項(xiàng),比如以debug模式運(yùn)行還是以daemon模式運(yùn)行。如果以debug模式運(yùn)行,那么標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出大多數(shù)都是直接輸出到當(dāng)前終端上,如果是daemon形式運(yùn)行,那么錯(cuò)誤輸出和標(biāo)準(zhǔn)輸出可能會(huì)被分別輸出到兩個(gè)不同的配置文件中去 // 連工作目錄都是一個(gè)配置項(xiàng)目,通過php函數(shù)chdir可以修改當(dāng)前工作目錄 chdir( $dir );
[原文地址:https://blog.ti-node.com/blog...]
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/29350.html
摘要:主進(jìn)程退出子進(jìn)程繼續(xù)執(zhí)行給進(jìn)程重新起個(gè)名字加入我們出個(gè)子進(jìn)程就可以搞定這些任務(wù),那么出個(gè)子進(jìn)程,同時(shí)父進(jìn)程要負(fù)責(zé)這個(gè)子進(jìn)程的狀態(tài)等。 [原文地址:https://blog.ti-node.com/blog...] 干巴巴地叨逼叨了這么久,時(shí)候表演真正的技術(shù)了! 做個(gè)高端點(diǎn)兒的玩意吧,加入我們要做一個(gè)任務(wù)系統(tǒng),這個(gè)系統(tǒng)可以在后臺(tái)幫我們完成一大波(注意是一大波)數(shù)據(jù)的處理,那么我們自然想到...
摘要:表示該工作應(yīng)該在每個(gè)月日上午運(yùn)行這里還有一些其他的示例表示工作應(yīng)該在星期三每分鐘運(yùn)行一次。表示該工作應(yīng)該每天在凌晨點(diǎn)和點(diǎn)運(yùn)行兩次。方法調(diào)用的實(shí)例作為唯一的參數(shù),這是用于記錄您提供的作業(yè)的計(jì)劃任務(wù)管理器,并決定每次守護(hù)進(jìn)程應(yīng)該運(yùn)行什么。 譯文GitHub https://github.com/yuansir/diving-laravel-zh 原文鏈接 https://divinglar...
摘要:所以我們只說的多進(jìn)程,至于多線程就暫時(shí)放到一邊兒。出來新進(jìn)程則成為子進(jìn)程,原進(jìn)程則成為父進(jìn)程,子進(jìn)程擁有父進(jìn)程的副本。在父進(jìn)程中返回子進(jìn)程的進(jìn)程,在子進(jìn)程內(nèi)部本身返回?cái)?shù)字。 [原文地址:https://blog.ti-node.com/blog...] 實(shí)際上PHP是有多線程的,只是很多人不常用。使用PHP的多線程首先需要下載安裝一個(gè)線程安全版本(ZTS版本)的PHP,然后再安裝pec...
摘要:原文地址在初探先從一個(gè)簡(jiǎn)單的服務(wù)器開始中依次講解了三個(gè)逐漸進(jìn)步的服務(wù)器只能服務(wù)于一個(gè)客戶端的服務(wù)器利用可以服務(wù)于多個(gè)客戶端的額服務(wù)器利用預(yù)派生進(jìn)程服務(wù)于多個(gè)客戶端的服務(wù)器最后一種服務(wù)器的進(jìn)程模型基本上的大概原理其實(shí)跟我們常用的是非常 [原文地址:https://blog.ti-node.com/blog...] 在<PHP socket初探 --- 先從一個(gè)簡(jiǎn)單的socket服務(wù)器開始...
閱讀 3785·2021-08-30 09:47
閱讀 3728·2019-08-30 15:56
閱讀 688·2019-08-30 14:18
閱讀 707·2019-08-29 16:17
閱讀 2077·2019-08-29 11:07
閱讀 654·2019-08-26 13:53
閱讀 3461·2019-08-26 10:26
閱讀 2505·2019-08-23 18:30