成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

PHP多進(jìn)程初探 --- 再次談daemon進(jìn)程

fox_soyoung / 2019人閱讀

摘要:因?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卸載,非常悲劇。

最后一個(gè)問題是,要在第一次fork后設(shè)置umask(0),避免權(quán)限上的一些問題。所以較為完整的代碼如下:
// 設(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

相關(guān)文章

  • PHP進(jìn)程初探 --- 利用進(jìn)程開發(fā)點(diǎn)兒東西吧

    摘要:主進(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ù)的處理,那么我們自然想到...

    huaixiaoz 評(píng)論0 收藏0
  • 剖析 Laravel 計(jì)劃任務(wù)--初探

    摘要:表示該工作應(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...

    mo0n1andin 評(píng)論0 收藏0
  • PHP進(jìn)程初探 --- 開篇

    摘要:所以我們只說的多進(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...

    wh469012917 評(píng)論0 收藏0
  • PHP socket初探 --- select系統(tǒng)調(diào)用

    摘要:原文地址在初探先從一個(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ù)器開始...

    springDevBird 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<