目錄
0.前言
1.fork()創(chuàng)建子進(jìn)程講解
1.1fork()的簡(jiǎn)單介紹
1.2 創(chuàng)建子進(jìn)程詳解
1.2.1 如何理解fork創(chuàng)建子進(jìn)程
1.2.2 子進(jìn)程的PCB以及子進(jìn)程的代碼和數(shù)據(jù)
1.2.3為什么要共享&&寫(xiě)時(shí)拷貝
1.2.4?什么時(shí)候發(fā)生寫(xiě)時(shí)拷貝
1.3 fork函數(shù)返回值詳解
1.3.1引入fork返回值的作用
1.3.2 fork返回執(zhí)行邏輯剖析
1.3.3 小問(wèn)題補(bǔ)充
2.進(jìn)程狀態(tài)
2.1 進(jìn)程狀態(tài)引入
2.2 初識(shí)進(jìn)程中的狀態(tài)
2.3 R狀態(tài)解析
2.4 S狀態(tài)解析
2.4.1 S狀態(tài)的具體表現(xiàn)
2.4.2 CPU視角下的進(jìn)程等待
2.4.3 進(jìn)程卡死與進(jìn)程等待
2.5 D狀態(tài)解析
2.5.1 D狀態(tài)對(duì)比S狀態(tài)
2.5.2 生動(dòng)例子說(shuō)明D狀態(tài)意義
2.5.3 D狀態(tài)進(jìn)程問(wèn)題補(bǔ)充
2.6 T狀態(tài)介紹
2.7 t狀態(tài)介紹
2.8 X狀態(tài)介紹
2.9 Z狀態(tài)詳解
2.9.1 故事講解
2.9.2 事例類比進(jìn)程
2.10 進(jìn)程狀態(tài)總結(jié)
3. 在實(shí)踐中驗(yàn)證各個(gè)狀態(tài)
3.1 R狀態(tài)驗(yàn)證
3.2 S狀態(tài)驗(yàn)證
3.2.1 S狀態(tài)簡(jiǎn)單示例
3.2.2 S狀態(tài)復(fù)雜示例
3.3 T暫停狀態(tài)演示
3.3.1 T狀態(tài)進(jìn)程演示
3.3.2 前臺(tái)運(yùn)行&&后臺(tái)運(yùn)行
3.4* Z僵尸狀態(tài)演示
3.4.1 Z狀態(tài)進(jìn)程的回收
3.4.2 Z狀態(tài)驗(yàn)證1
?3.4.2 Z狀態(tài)驗(yàn)證2
0.前言
上一篇博客我們解決了什么是進(jìn)程(大格局了解進(jìn)程),尤其是對(duì)進(jìn)程運(yùn)行原理以及進(jìn)程控制塊PCB的理解,本文我們將以此為基礎(chǔ),著重講解子進(jìn)程的創(chuàng)建和進(jìn)程的狀態(tài),絕對(duì)也是干貨爆滿。
上篇博客鏈接:(1條消息) [入門(mén)篇]手把手帶你理解進(jìn)程在計(jì)算機(jī)中到底是什么!(Linux系統(tǒng)新手必看哦)_yuyulovespicy的博客-CSDN博客https://blog.csdn.net/qq_63992711/article/details/127517217?spm=1001.2014.3001.5502
本篇博客的所有相關(guān)實(shí)驗(yàn)代碼已上傳至Gitee,請(qǐng)君自?。? practice8 · onlookerzy123456qwq/Linuxtwo - 碼云 - 開(kāi)源中國(guó) (gitee.com)https://gitee.com/onlookerzy123456qwq/linuxtwo/tree/master/practice8下面正文開(kāi)始?。?!
1.fork()創(chuàng)建子進(jìn)程講解
1.1fork()的簡(jiǎn)單介紹
我們之前創(chuàng)建進(jìn)程的方式就是在一個(gè)路徑下 ./proc 執(zhí)行一個(gè)程序,將該程序加載到內(nèi)存執(zhí)行起來(lái),在程序中就會(huì)創(chuàng)建一個(gè)進(jìn)程。當(dāng)然我們也知道創(chuàng)建一個(gè)進(jìn)程就意味著在內(nèi)存中載入了該進(jìn)程的代碼和數(shù)據(jù),以及OS為該進(jìn)程創(chuàng)建的進(jìn)程控制塊task_struct。
因?yàn)?進(jìn)程==相關(guān)內(nèi)核數(shù)據(jù)結(jié)構(gòu)+進(jìn)程的資源代碼和數(shù)據(jù)
以上我們是用戶主動(dòng)執(zhí)行創(chuàng)建一個(gè)進(jìn)程,那我們?nèi)绾卧诖a當(dāng)中,在一個(gè)進(jìn)程當(dāng)中執(zhí)行語(yǔ)句讓進(jìn)程自己去再創(chuàng)建一個(gè)新的進(jìn)程呢?這就用到了我們的fork了?。?!
首先fork()是Linux操作系統(tǒng)中的一個(gè)系統(tǒng)調(diào)用接口,其作用是創(chuàng)建子進(jìn)程,創(chuàng)建進(jìn)程就意味著為這個(gè)子進(jìn)程創(chuàng)建一個(gè)PCB數(shù)據(jù)結(jié)構(gòu),但是這里子進(jìn)程是比較特殊的,子進(jìn)程和父進(jìn)程有密切的關(guān)系,子進(jìn)程的代碼和數(shù)據(jù)其實(shí)是很有講究的,這個(gè)我們?cè)诤竺嬷v述。
?總之我們明確一件事,fork的作用就是能夠在代碼運(yùn)行時(shí)創(chuàng)建一個(gè)新的進(jìn)程,我們將之稱之為當(dāng)前進(jìn)程的子進(jìn)程,其實(shí)也就意味著現(xiàn)在內(nèi)存中多了一個(gè)進(jìn)程,即多了一個(gè)執(zhí)行流。下面我們來(lái)體驗(yàn)一下fork的作用:
展示1:
?展示2:
展示3:
打印了兩份消息,打印出來(lái)的消息是不同的,?可是進(jìn)程中的代碼當(dāng)中我們只cout了一次,為什么能打印出來(lái)兩種內(nèi)容呢,我們觀察pid ppid發(fā)現(xiàn)這是父子進(jìn)程的關(guān)系。其實(shí)真正的原因只有一個(gè),那就是Fork創(chuàng)建的子進(jìn)程在運(yùn)行,而原來(lái)的父進(jìn)程也在運(yùn)行,這是兩個(gè)進(jìn)程都在運(yùn)行,即兩個(gè)程序在“同時(shí)”跑!一個(gè)進(jìn)程的的在打印它的PID,PPID,另一個(gè)進(jìn)程在打印它的PID,PPID。
1.2 創(chuàng)建子進(jìn)程詳解
1.2.1 如何理解fork創(chuàng)建子進(jìn)程
1.我們 ./proc 運(yùn)行起來(lái)一個(gè)程序 或者是 在進(jìn)程中 fork()?, 其本質(zhì)上都是一樣的,在操作系統(tǒng)的層面都是在創(chuàng)建一個(gè)進(jìn)程,而且在上面創(chuàng)建進(jìn)程的方式,是沒(méi)有差別的。
2. fork()的本質(zhì)是創(chuàng)建進(jìn)程,那就一定會(huì)導(dǎo)致一個(gè)結(jié)果:系統(tǒng)里多了一個(gè)進(jìn)程,而進(jìn)程是什么,進(jìn)程==與進(jìn)程相關(guān)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)task_struct + 進(jìn)程的代碼和數(shù)據(jù)。所以創(chuàng)建進(jìn)程后理應(yīng)在系統(tǒng)中多了一套子進(jìn)程自己的PCB以及自己的代碼和數(shù)據(jù)。
1.2.2 子進(jìn)程的PCB以及子進(jìn)程的代碼和數(shù)據(jù)
這個(gè)創(chuàng)建的PCB其實(shí)就是針對(duì)fork()創(chuàng)建出的子進(jìn)程,OS對(duì)之創(chuàng)建一個(gè)屬于這個(gè)子進(jìn)程的task_struct,從而OS可以管理task_struct數(shù)據(jù)結(jié)構(gòu)的方式來(lái)管理這個(gè)子進(jìn)程。這是一定的。
那話說(shuō)回來(lái),那子進(jìn)程的代碼和數(shù)據(jù)呢???答:子進(jìn)程它的代碼和數(shù)據(jù),在默認(rèn)情況下,會(huì)“繼承”父進(jìn)程的代碼和數(shù)據(jù)。當(dāng)然我們補(bǔ)充一點(diǎn):內(nèi)核數(shù)據(jù)結(jié)構(gòu)task_Struct也會(huì)以父進(jìn)程的task_Struct為模板,初始化子進(jìn)程的task_Struct。
我們?nèi)绾卫斫膺@一點(diǎn)呢?我們舉一個(gè)生動(dòng)的例子:
話說(shuō)你爸其實(shí)是一個(gè)鞋廠的老板,舉個(gè)例子,是ERKE鴻星爾克品牌的專業(yè)代工廠,從你誕生的那一刻起,基本上要發(fā)生兩件事情。第一件事情,你的相貌會(huì)以你爹為模板為基礎(chǔ),我們要相信基因的力量,進(jìn)而長(zhǎng)出自己的模樣,第二件事情,你以后也大概率會(huì)子承父業(yè),繼承你爹的鞋廠,繼續(xù)當(dāng)廠二代。(當(dāng)然后續(xù)也可能你會(huì)自主創(chuàng)業(yè),比如你自己又開(kāi)了個(gè)自己品牌的鞋廠,這是也很有可能發(fā)生的,確實(shí)存在這種情況,但是我們暫不考慮)
相貌會(huì)以你爹為模板長(zhǎng)出自己的樣子,這是刻在自己基因里的東西,一定會(huì)影響到你的。而后續(xù)你的事業(yè),大概會(huì)繼承你爹開(kāi)的的鞋廠做廠二代。
這里我們要看到你和你爹鞋廠用的是同一個(gè)鞋廠,你和你爹的五官樣貌卻是真真實(shí)實(shí)有兩份的,不過(guò)都有繼承。這里的鞋廠&&五官樣貌,可以類比子進(jìn)程的代碼和數(shù)據(jù)&&內(nèi)核數(shù)據(jù)結(jié)構(gòu)。子進(jìn)程的內(nèi)核數(shù)據(jù)結(jié)構(gòu)task_struct就是子進(jìn)程的“五官樣貌”,子進(jìn)程一定會(huì)以父進(jìn)程的task_struct為模板來(lái)創(chuàng)建出自己的task_struct。子進(jìn)程的代碼和數(shù)據(jù)就是鞋廠,這個(gè)鞋廠是父進(jìn)程開(kāi)創(chuàng)的,父子共用的是同一個(gè)鞋廠,子進(jìn)程的代碼和數(shù)據(jù)用的是父進(jìn)程的那一份,此時(shí)父子進(jìn)程共用同一份代碼和數(shù)據(jù)!??!
當(dāng)然如果后續(xù)你翅膀硬了,獨(dú)立自主性強(qiáng)了,你自己開(kāi)辦了自己品牌的新鞋廠,這當(dāng)然是可以的,此時(shí)就可以類比為,子進(jìn)程和父進(jìn)程不再共享同一份代碼和數(shù)據(jù),而是子進(jìn)程也有了獨(dú)屬于自己的一份代碼和數(shù)據(jù)。
總結(jié)一下,剛剛被fork創(chuàng)建出的子進(jìn)程,其內(nèi)核數(shù)據(jù)結(jié)構(gòu)task_struct是以父進(jìn)程為模板創(chuàng)建出自己的PCB的,其代碼和數(shù)據(jù)是一開(kāi)始是共享父進(jìn)程的代碼和數(shù)據(jù)的,但是后續(xù)會(huì)因某種特定情況的發(fā)生不再共享,而父子進(jìn)程各獨(dú)占一份代碼和數(shù)據(jù)。
那么這種特定的情況到底是什么呢???那就是發(fā)生寫(xiě)入?。。。。。。。。。。。?!
父子進(jìn)程一開(kāi)始是共享同一份代碼和數(shù)據(jù)的,如果此時(shí)某個(gè)進(jìn)程(無(wú)論父進(jìn)程還是子進(jìn)程)在執(zhí)行的時(shí)候,對(duì)這同一份數(shù)據(jù)區(qū)做出了寫(xiě)入操作,即修改了這份共享的數(shù)據(jù)區(qū)里的內(nèi)容,那就會(huì)在寫(xiě)入之前把這塊數(shù)據(jù)區(qū)先拷貝一份,父子進(jìn)程不再共享同一塊數(shù)據(jù)區(qū),而是讓這兩個(gè)進(jìn)程分別獨(dú)占一份數(shù)據(jù)區(qū),然后再哪個(gè)進(jìn)程對(duì)他自己的數(shù)據(jù)區(qū)進(jìn)行寫(xiě)入。這個(gè)現(xiàn)象還有一個(gè)響當(dāng)當(dāng)?shù)拿?---寫(xiě)時(shí)拷貝。
同理,如果父子進(jìn)程現(xiàn)在在共享同一份代碼段,當(dāng)某個(gè)進(jìn)程(無(wú)論父還是子)對(duì)這份共享的一個(gè)代碼段進(jìn)行了寫(xiě)入,即修改這塊共享的代碼段,那么此時(shí)就會(huì)在修改代碼區(qū)之前,先把這塊代碼段拷貝一份,此時(shí)加上原來(lái)共享的代碼段現(xiàn)在就有兩份代碼區(qū),讓父子進(jìn)程各自獨(dú)用一塊代碼段,然后父進(jìn)程/子進(jìn)程再各自該修改就修改自己的那份代碼段就可以了。
不過(guò),我們現(xiàn)在這個(gè)程度只能在父子進(jìn)程運(yùn)行的時(shí)候只能發(fā)生數(shù)據(jù)的改變,而不能發(fā)生代碼的改變,所以就是我們只能觀察到數(shù)據(jù)區(qū)的寫(xiě)時(shí)拷貝,而觀察不到代碼區(qū)的寫(xiě)時(shí)拷貝。所以本篇博文主要討論數(shù)據(jù)的寫(xiě)時(shí)拷貝。
1.2.3為什么要共享&&寫(xiě)時(shí)拷貝
首先我們提出兩個(gè)問(wèn)題:
為什么在要發(fā)生寫(xiě)時(shí)拷貝呢,讓父子進(jìn)程一直共享同一份代碼和數(shù)據(jù)不可以嗎?
fork創(chuàng)建出的子進(jìn)程為什么要和父進(jìn)程共享同一份代碼和數(shù)據(jù)呢,直接在一開(kāi)始就拷貝一份代碼和數(shù)據(jù),讓他們直接各自獨(dú)占一份代碼和數(shù)據(jù)不也很好嗎?
我想說(shuō)的是:操作系統(tǒng)這樣設(shè)計(jì)出來(lái)父子進(jìn)程共享代碼和數(shù)據(jù)以及后續(xù)寫(xiě)時(shí)拷貝機(jī)制是有道理的。
問(wèn)題一:為什么要發(fā)生寫(xiě)時(shí)拷貝?
答:為了維護(hù)進(jìn)程的獨(dú)立性,不要讓進(jìn)程間的運(yùn)行相互干擾。
我們首先舉一個(gè)例子來(lái)說(shuō)明一下系統(tǒng)中進(jìn)程的獨(dú)立性:
我們?cè)赪indows操作系統(tǒng)中開(kāi)著微信聊天,QQ聊天,網(wǎng)易云音樂(lè),CSDN寫(xiě)博客這幾個(gè)進(jìn)程,那現(xiàn)在如果網(wǎng)易云音樂(lè)這個(gè)進(jìn)程掛掉了,即現(xiàn)在網(wǎng)易云閃退了,會(huì)影響我們繼續(xù)寫(xiě)CSDN博客嗎?事實(shí)上是不會(huì)的,網(wǎng)易云進(jìn)程掛掉并不影響我們CSDN這個(gè)進(jìn)程。QQ進(jìn)程掛掉也不影響我們?cè)谖⑿胚M(jìn)程的聊天。?同樣的微信掛掉,也不會(huì)影響我們QQ聊天的繼續(xù)。
所以這說(shuō)明了一件事->進(jìn)程具有獨(dú)立性?。?!進(jìn)程和進(jìn)程之間是互不影響的。一個(gè)進(jìn)程掛掉不會(huì)影響另一個(gè)進(jìn)程。
如果父子當(dāng)中的一個(gè)進(jìn)程會(huì)寫(xiě)入影響他們共享的數(shù)據(jù)區(qū),此時(shí)父子進(jìn)程還共享同一份數(shù)據(jù)的話,那其實(shí)就不能維護(hù)父進(jìn)程和子進(jìn)程之間的獨(dú)立性了。(假設(shè)現(xiàn)在是子進(jìn)程要寫(xiě)入數(shù)據(jù)區(qū),而父進(jìn)程仍然需要保持?jǐn)?shù)據(jù)區(qū)的不變,此時(shí)父子進(jìn)程要維護(hù)獨(dú)立性的話,必須給子進(jìn)程再單獨(dú)開(kāi)辟一塊數(shù)據(jù)區(qū),讓他們單獨(dú)占有一個(gè)數(shù)據(jù)區(qū),子進(jìn)程寫(xiě)入自己的數(shù)據(jù)區(qū),父進(jìn)程仍然可以使用原來(lái)不能改變的數(shù)據(jù)區(qū),這樣就維護(hù)住了進(jìn)程之間的獨(dú)立性)
問(wèn)題二:父子進(jìn)程為什么一開(kāi)始要共享代碼和數(shù)據(jù)?
答:降低創(chuàng)建進(jìn)程的成本以及提高創(chuàng)建進(jìn)程的效率。
如果我們每次fork創(chuàng)建子進(jìn)程后,都要100%發(fā)生數(shù)據(jù)區(qū)和代碼區(qū)的寫(xiě)時(shí)拷貝,那其實(shí)創(chuàng)建子進(jìn)程的成本是很高的,而且很多情況下,你創(chuàng)建的子進(jìn)程是不需要發(fā)生寫(xiě)入,不需要改變數(shù)據(jù)區(qū)和代碼區(qū)的,做的僅僅是讀取數(shù)據(jù)的工作,此時(shí)單獨(dú)給子進(jìn)程單獨(dú)拷貝一份數(shù)據(jù)和代碼其實(shí)是非常浪費(fèi)的!而這種情況下讓父子進(jìn)程共享同一塊數(shù)據(jù)區(qū)和代碼區(qū),成本就會(huì)低很多且不影響父子進(jìn)程的運(yùn)行。后面父子進(jìn)程如果要發(fā)生寫(xiě)入,我們?cè)賹?xiě)時(shí)拷貝單獨(dú)拷貝也不遲嘛。
至此,我們就明白了子進(jìn)程創(chuàng)建的實(shí)質(zhì),子進(jìn)程的task_struct以及子進(jìn)程的代碼和數(shù)據(jù),對(duì)父進(jìn)程的“繼承”性,以及代碼數(shù)據(jù)共享以及寫(xiě)時(shí)拷貝機(jī)制。
現(xiàn)在有了對(duì)子進(jìn)程創(chuàng)建機(jī)制的理解,在此基礎(chǔ)上,我們?cè)偌?xì)致了解一下可以創(chuàng)建子進(jìn)程的fork函數(shù)。
1.2.4?什么時(shí)候發(fā)生寫(xiě)時(shí)拷貝
其實(shí)發(fā)生寫(xiě)時(shí)拷貝的時(shí)機(jī)很好把握,就一句話,無(wú)論是父進(jìn)程還是子進(jìn)程,只有他們其中任一個(gè)進(jìn)程,對(duì)一個(gè)他們都能看到、都能使用或者更準(zhǔn)確的說(shuō)是都能獲取、都能寫(xiě)入的變量,進(jìn)行寫(xiě)入,就會(huì)發(fā)生寫(xiě)時(shí)拷貝。
分解一下:
who does :父進(jìn)程or子進(jìn)程任一個(gè)。
who does whom : 父子進(jìn)程都可以看到,都可以獲取到,都可以對(duì)之讀寫(xiě)的變量。
when :當(dāng)父進(jìn)程/子進(jìn)程任一個(gè)寫(xiě)入該種變量時(shí)發(fā)生。
所以對(duì)下面代碼當(dāng)中:
對(duì)于全局變量g_val,在fork創(chuàng)建子進(jìn)程之前創(chuàng)建的變量child_parent,這些都是父進(jìn)程和子進(jìn)程都可以看到,都可以獲取,都可以對(duì)之寫(xiě)入的變量。當(dāng)在寫(xiě)入 g_val , child_parent 變量時(shí)都能發(fā)生寫(xiě)時(shí)拷貝。
而對(duì)于tmp_child,tmp_parent,這些父進(jìn)程或子進(jìn)程自己?jiǎn)为?dú)定義的變量,比如父進(jìn)程就獲取使用不到子進(jìn)程自己定義的變量tmp_child,子進(jìn)程就獲取使用不到父進(jìn)程自己定義的變量tmp_parent。所以對(duì)這些變量寫(xiě)入,tmp_child = 1 , tmp_parent = 2 等等,都是不會(huì)發(fā)生寫(xiě)時(shí)拷貝的。
1.3 fork函數(shù)返回值詳解
1.3.1引入fork返回值的作用
首先問(wèn)一個(gè)問(wèn)題:我們的父進(jìn)程和子進(jìn)程是共享同一段代碼的,大多數(shù)執(zhí)行的也是同一段代碼,那父子進(jìn)程是不是只能做一樣的事情呢?
這樣其實(shí)是不合理的!我們父進(jìn)程創(chuàng)建出子進(jìn)程,其目的肯定是讓子進(jìn)程去完成什么不同的任務(wù),然后子進(jìn)程再把完成任務(wù)的反饋信息(子進(jìn)程的退出信息)告訴父進(jìn)程,這實(shí)際上才是父進(jìn)程創(chuàng)建子進(jìn)程的目的以及整個(gè)父子進(jìn)程間運(yùn)行的邏輯,父子進(jìn)程要做的事情(執(zhí)行的代碼)肯定是要不一樣的。
那我們如何讓執(zhí)行同一段代碼的父子進(jìn)程干不同的事情(執(zhí)行不同的代碼)呢?
這時(shí)候就要靠fork的返回值了?。?!
這里附上fork返回值官方文檔介紹
翻譯:成功創(chuàng)建子進(jìn)程的話,那子進(jìn)程的PID值就會(huì)返回給父進(jìn)程,0就會(huì)被返回給新創(chuàng)建的子進(jìn)程。如果創(chuàng)建子進(jìn)程失敗的話,-1就會(huì)被返回給父進(jìn)程,當(dāng)然子進(jìn)程也不會(huì)被創(chuàng)建也就沒(méi)有返回,同時(shí)全局錯(cuò)誤碼errno也會(huì)被設(shè)置。?
fork對(duì)子進(jìn)程返回0值,對(duì)父進(jìn)程返回子進(jìn)程PID值,這樣就可以根據(jù)這個(gè)返回值進(jìn)行執(zhí)行代碼的分流,示例如下:
可是這樣理解并不全面,下面我再帶領(lǐng)大家細(xì)品fork創(chuàng)建子進(jìn)程的這個(gè)過(guò)程的全邏輯。
1.3.2 fork返回執(zhí)行邏輯剖析
如何理解兩個(gè)返回值?返回這兩個(gè)返回值期間具體到底發(fā)生了什么?為什么要分別返回兩個(gè)返回值呢?
返回兩個(gè)返回值并不是兩次返回這么簡(jiǎn)單,中間還牽扯著寫(xiě)時(shí)拷貝的問(wèn)題。
給父子進(jìn)程返回兩個(gè)不同的返回值,是為了讓父子進(jìn)程即使共用同一個(gè)代碼區(qū),也能執(zhí)行不同的邏輯。
下面我們做細(xì)致的分析:
解答第一個(gè)問(wèn)題->:fork函數(shù)return期間發(fā)生了什么:
我們首先要明白一個(gè)問(wèn)題,一個(gè)函數(shù)在return之前,他的核心邏輯是不是已經(jīng)執(zhí)行完了?當(dāng)然是這樣,函數(shù)核心工作完成了,最后再return一個(gè)值給上一層反饋嘛~ 。那么其實(shí)也就是說(shuō)fork()函數(shù)要return值的時(shí)候,其實(shí)fork函數(shù)的核心已經(jīng)執(zhí)行完了,即現(xiàn)在系統(tǒng)中已經(jīng)多了一個(gè)進(jìn)程,父進(jìn)程的子進(jìn)程就已經(jīng)被創(chuàng)建出來(lái)了。
即fork返回的時(shí)候,此時(shí)父進(jìn)程和子進(jìn)程就已經(jīng)在共享同一份代碼段和數(shù)據(jù)區(qū)了。但是故事的轉(zhuǎn)機(jī)體現(xiàn)在fork的返回值上?。。。?!當(dāng)返回值返回到某一個(gè)進(jìn)程的時(shí)候,(此時(shí)他們還共用同一塊數(shù)據(jù)區(qū)),return值是不是會(huì)對(duì)數(shù)據(jù)區(qū)發(fā)生寫(xiě)入?。?!是的?。?!此時(shí)他們共享的數(shù)據(jù)區(qū)中的id變量被寫(xiě)入了,其實(shí)無(wú)論id被賦值成0還是子進(jìn)程PID,本質(zhì)上都是在數(shù)據(jù)區(qū)上的寫(xiě)入?。?!此時(shí)就會(huì)觸發(fā)寫(xiě)時(shí)拷貝機(jī)制?。。。?!子進(jìn)程和父進(jìn)程各自獨(dú)享一塊數(shù)據(jù)區(qū)!此時(shí)子進(jìn)程可以對(duì)他數(shù)據(jù)區(qū)里的id賦值0,父進(jìn)程在他的數(shù)據(jù)區(qū)的id賦值子進(jìn)程的PID值,從而維護(hù)住父子進(jìn)程的獨(dú)立性。
我們看如下代碼:
解決第二個(gè)問(wèn)題->: 為什么給父子進(jìn)程返回兩個(gè)不同的返回值:
現(xiàn)在如果是處于return之后(當(dāng)然已經(jīng)發(fā)生了寫(xiě)時(shí)拷貝)父進(jìn)程的數(shù)據(jù)區(qū)的id值為子進(jìn)程PID,子進(jìn)程的數(shù)據(jù)區(qū)的id值為0,那我們此時(shí)就可以利用父子進(jìn)程各自數(shù)據(jù)區(qū)中不同的id值(本質(zhì)是fork返回值),進(jìn)行if else分流,執(zhí)行不同的邏輯。再說(shuō)的明白一點(diǎn),id賦值fork的返回值后,其實(shí)就是此時(shí)父進(jìn)程數(shù)據(jù)區(qū)的id==子進(jìn)程PID,子進(jìn)程數(shù)據(jù)區(qū)的id==0, 此時(shí)父進(jìn)程再往下執(zhí)行就是執(zhí)行else(id>0)后面的邏輯,子進(jìn)程就只想if(id==)后面的邏輯,不過(guò)他們本質(zhì)還是在共享使用同一份代碼區(qū),只不過(guò)他們被(id值)fork的返回值,分流執(zhí)行不同的邏輯了?。。?/strong>
如此我們就解決了即使父子進(jìn)程共享同一段代碼區(qū),父子進(jìn)程仍可執(zhí)行不同邏輯的這個(gè)痛點(diǎn)了,破局之點(diǎn)就在fork的返回值以及父子進(jìn)程的寫(xiě)時(shí)拷貝機(jī)制?。?!
1.3.3 小問(wèn)題補(bǔ)充
那fork創(chuàng)建出來(lái)了子進(jìn)程,加上現(xiàn)在的父進(jìn)程是有兩個(gè)進(jìn)程,就是有兩個(gè)執(zhí)行流了,那先執(zhí)行父子哪個(gè)進(jìn)程呢?
我們觀察到的現(xiàn)象是父進(jìn)程先得到了執(zhí)行,其實(shí)并不是這樣的,先執(zhí)行父進(jìn)程還是子進(jìn)程這其實(shí)是由環(huán)境有關(guān)的,而且其實(shí)先執(zhí)行誰(shuí)都沒(méi)關(guān)系,畢竟父子進(jìn)程之間都是有獨(dú)立性的,而且進(jìn)程的執(zhí)行也是在運(yùn)行隊(duì)列上排隊(duì)接受CPU僅一個(gè)時(shí)間片的多次來(lái)回執(zhí)行,誰(shuí)先誰(shuí)后這個(gè)問(wèn)題也并不是特別的重要。
2.進(jìn)程狀態(tài)
2.1 進(jìn)程狀態(tài)引入
如圖, 我們先淺顯的描述一遍:一個(gè)進(jìn)程在被創(chuàng)建出來(lái)時(shí)就是初始態(tài),他可以運(yùn)行在等待CPU時(shí)就是就緒態(tài),在運(yùn)行的時(shí)候就是運(yùn)行態(tài),條件不足不可以運(yùn)行就無(wú)法就緒,就會(huì)去掛起等待(阻塞)。
我們不禁要問(wèn),什么是就緒,什么是運(yùn)行,這兩個(gè)態(tài)在系統(tǒng)當(dāng)中具體是什么樣的,什么是掛起,為什么要等待掛起/阻塞等待(等待,阻塞,掛起可以暫時(shí)理解為一件事)呢?一個(gè)進(jìn)程從創(chuàng)建到執(zhí)行的全過(guò)程中到底要經(jīng)歷什么呢?進(jìn)程的狀態(tài)有哪些呢?如何查看進(jìn)程狀態(tài)呢?每個(gè)狀態(tài)存在又代表了什么呢?下面我們將一一解答!
2.2 初識(shí)進(jìn)程中的狀態(tài)
在Linux操作系統(tǒng)中,進(jìn)程主要有 R狀態(tài)(運(yùn)行狀態(tài)),S狀態(tài)(等待/掛起/阻塞狀態(tài)/淺度睡眠狀態(tài)),D狀態(tài)(深度睡眠狀態(tài)/不可中斷狀態(tài)),T狀態(tài)(暫停狀態(tài)),t狀態(tài)(跟蹤暫停狀態(tài)),X狀態(tài)(死亡狀態(tài)),Z狀態(tài)(僵尸狀態(tài))。
英文解釋為 R-running , S-sleeping , D-disk sleep , T-stopped , t-tracing stop , X-dead , Z-zombie 。
進(jìn)程的狀態(tài)信息存儲(chǔ)在哪里呢?
那必然是該進(jìn)程的task_struct(進(jìn)程控制塊PCB)當(dāng)中,task_struct里面存儲(chǔ)著進(jìn)程的屬性信息嘛~?
進(jìn)程狀態(tài)的意義是什么呢?
首先進(jìn)程狀態(tài)描述的是你進(jìn)程現(xiàn)在正在干什么,比如你現(xiàn)在處于R狀態(tài),就證明你這個(gè)進(jìn)程正在運(yùn)行(或即將運(yùn)行),比如你處于S狀態(tài),就證明你這個(gè)進(jìn)程正在掛起等待,比如你這個(gè)進(jìn)程標(biāo)志處于T狀態(tài),就說(shuō)明這個(gè)進(jìn)程是正在暫停的。
所以進(jìn)程狀態(tài)可以描述出你這個(gè)進(jìn)程現(xiàn)在在干什么,可以方便操作系統(tǒng)快速判斷進(jìn)程,從而讓OS快速完成特定的功能。比如調(diào)度,你現(xiàn)在一個(gè)進(jìn)程處于S等待/掛起狀態(tài),操作系統(tǒng)就可以快速判斷出你這個(gè)進(jìn)程實(shí)際上正在等待什么東西,此時(shí)OS就不會(huì)把你調(diào)度去CPU運(yùn)行。比如你現(xiàn)在進(jìn)程PCB標(biāo)志X死亡狀態(tài),說(shuō)明這個(gè)進(jìn)程已經(jīng)死亡了,OS此時(shí)看到這個(gè)進(jìn)程的X狀態(tài)信息后,也不會(huì)再調(diào)度你了。
所以進(jìn)程狀態(tài),本質(zhì)上就是一種分類?。。。?/strong>
2.3 R狀態(tài)解析
首先我們知道進(jìn)程要被CPU執(zhí)行,進(jìn)程的PCB就要被調(diào)度到運(yùn)行隊(duì)列run_queue上排隊(duì),之后接受CPU一個(gè)一個(gè)時(shí)間片的來(lái)回排隊(duì)調(diào)度運(yùn)行。
R狀態(tài)(running)首先他叫運(yùn)行狀態(tài),但是如果一個(gè)進(jìn)程PCB顯示它正處于R狀態(tài),那能不能說(shuō)明這個(gè)進(jìn)程就正在占有CPU資源呢?其實(shí)這樣并不準(zhǔn)確,所謂的R狀態(tài),不僅只是運(yùn)行態(tài),還包括就緒態(tài)!其實(shí)是就緒態(tài)和運(yùn)行態(tài)的集合!進(jìn)程處于R狀態(tài)不能說(shuō)明這個(gè)進(jìn)程一定正在占有CPU資源(運(yùn)行態(tài)),也有可能這個(gè)進(jìn)程在運(yùn)行隊(duì)列run_queue中等待CPU資源(就緒態(tài))。在運(yùn)行隊(duì)列中等待CPU資源或進(jìn)程正在享用CPU資源,都可以表明這個(gè)進(jìn)程可以運(yùn)行,即都可以稱R狀態(tài)。
也就是說(shuō)進(jìn)程表明它可以被執(zhí)行了,也就是說(shuō)他就會(huì)在運(yùn)行隊(duì)列run_queue中排隊(duì),或者它排到隊(duì)了,正在使用CPU資源。在Linux操作系統(tǒng)下,這兩種情況下進(jìn)程都是R狀態(tài)?。?!
2.4 S狀態(tài)解析
2.4.1 S狀態(tài)的具體表現(xiàn)
S狀態(tài)(sleeping),也可以稱之為掛起狀態(tài),等待狀態(tài),阻塞狀態(tài),淺度睡眠狀態(tài),這些名字都是有相應(yīng)道理的,空說(shuō)S狀態(tài)并不具體,下面我們通過(guò)幾個(gè)具體例子,了解進(jìn)程在處于S狀態(tài)時(shí),是為了什么,在干什么:
比如今天你今天在宿舍想打游戲,但是在打游戲前,你就必須要等待電腦開(kāi)機(jī),電腦不開(kāi)機(jī)你就暫時(shí)不具備打游戲的條件,要打游戲必須等待電腦資源開(kāi)機(jī)!
比如說(shuō)今天我這個(gè)進(jìn)程,今天想從網(wǎng)絡(luò)當(dāng)中讀取一個(gè)數(shù)據(jù),可是現(xiàn)在網(wǎng)絡(luò)上還沒(méi)有數(shù)據(jù),比如數(shù)據(jù)還沒(méi)有傳到網(wǎng)絡(luò)上,所以你這個(gè)進(jìn)程就得等待網(wǎng)絡(luò)資源響應(yīng),等到網(wǎng)絡(luò)上數(shù)據(jù)到了之后,進(jìn)程才會(huì)繼續(xù)執(zhí)行。因?yàn)檫M(jìn)程代碼的執(zhí)行邏輯就是先從網(wǎng)絡(luò)中獲取到數(shù)據(jù),然后處理數(shù)據(jù)嘛,讀取不到數(shù)據(jù)肯定不能往下執(zhí)行嘛~
比如說(shuō)今天進(jìn)程必須要從磁盤(pán)上讀取某個(gè)文件內(nèi)容,可是磁盤(pán)上現(xiàn)在沒(méi)有文件。再比如我這個(gè)進(jìn)程必須要往磁盤(pán)上寫(xiě)入一些數(shù)據(jù),可是磁盤(pán)現(xiàn)在已經(jīng)寫(xiě)滿了。進(jìn)程就得等待磁盤(pán)上有文件,進(jìn)程必須等待磁盤(pán)上有空閑空間出現(xiàn)時(shí),才能繼續(xù)運(yùn)行下去。再比如你這個(gè)進(jìn)程到了scanf,cin執(zhí)行邏輯的一行,那是不是如果鍵盤(pán)外設(shè)不進(jìn)行數(shù)據(jù)的輸入,進(jìn)程就無(wú)法讀取到數(shù)據(jù)也就無(wú)法繼續(xù)執(zhí)行,此時(shí)進(jìn)程就必須等待鍵盤(pán)資源了。當(dāng)進(jìn)程遇到外部資源條件不具備的時(shí)候,就必須得一直等待,等到某個(gè)資源條件具備,才會(huì)繼續(xù)執(zhí)行。
當(dāng)我們想完成某種任務(wù)時(shí),任務(wù)條件不具備,需要進(jìn)程進(jìn)行某種等待,(進(jìn)程無(wú)法執(zhí)行,什么事都干不了,就干等著),進(jìn)程等待的狀態(tài),在Linux中就叫S狀態(tài)/D狀態(tài)。(S,D狀態(tài)的區(qū)別我們?cè)贒狀態(tài)詳解時(shí)說(shuō)明,這里我們暫把S狀態(tài)和D狀態(tài)先理解為一種等待狀態(tài))
但是有沒(méi)有一種可能,現(xiàn)在整個(gè)宿舍里只有一臺(tái)電腦,不僅是你,你和你室友都想玩游戲,這當(dāng)然是有可能的,我和室友都想玩游戲,拋開(kāi)后面你和室友玩游戲搶鼠標(biāo)鍵盤(pán)的場(chǎng)景,現(xiàn)在是不是你和你的室友都要等待電腦的開(kāi)機(jī)呢?這當(dāng)然是,電腦不開(kāi)機(jī),不管是誰(shuí)都沒(méi)法玩游戲。例子中的你和室友其實(shí)就是一個(gè)一個(gè)的進(jìn)程,例子中的電腦就是進(jìn)程們要等待的某種資源,進(jìn)程們都在等待某種資源,這也就是說(shuō),某種資源不一定只被一個(gè)進(jìn)程等待,也有可能被多個(gè)進(jìn)程等待。
再具體一點(diǎn),系統(tǒng)中一個(gè)軟件Or一個(gè)硬件,一定會(huì)有不止一個(gè)進(jìn)程都想對(duì)之進(jìn)行訪問(wèn),一定有多個(gè)進(jìn)程PCB在等待這個(gè)外設(shè)準(zhǔn)備就緒,如要打印就要等待顯示器就緒,要讀取鍵盤(pán)就要等待用戶從鍵盤(pán)上輸入數(shù)據(jù),多個(gè)不同的進(jìn)程訪問(wèn)要訪問(wèn)同一個(gè)外設(shè),就得排隊(duì)訪問(wèn),這其實(shí)也是一個(gè)隊(duì)列,這個(gè)就叫做等待隊(duì)列wait_queue,這個(gè)類似于運(yùn)行隊(duì)列run_queue,不過(guò)run_queue是進(jìn)程們對(duì)CPU資源的排隊(duì)等待,wait_queue是進(jìn)程們對(duì)某外設(shè)資源軟件資源的等待。所以這也告訴我嗎進(jìn)程不只會(huì)排隊(duì)等待CPU資源,也可以排隊(duì)等待其他資源。
所以這里我們就可以具象化系統(tǒng)中正在等待的進(jìn)程了。如下圖所示:一個(gè)一個(gè)的進(jìn)程PCB需要使用某種外設(shè)資源,就需要等待某種外設(shè)資源就緒,也需要和其他進(jìn)程排隊(duì)等待,多個(gè)task_struct就組織在wait_queue等待隊(duì)列中,然后等待外設(shè)資源的就緒以及其他進(jìn)程對(duì)該外設(shè)資源的利用,這個(gè)task_struct在等待隊(duì)列中,等待享用到某資源的過(guò)程,我們說(shuō)該進(jìn)程處于S等待掛起狀態(tài)。
2.4.2 CPU視角下的進(jìn)程等待
我們?cè)購(gòu)?strong>CPU的角度考慮等待這件事,首先我們看CPU是怎么做的,當(dāng)一個(gè)進(jìn)程運(yùn)行到需要必須等待某種外設(shè)/軟件資源的時(shí)候,此時(shí)CPU就會(huì)將這個(gè)進(jìn)程PCB趕出run_queue運(yùn)行隊(duì)列,把該task_struct調(diào)度到對(duì)該外設(shè)/軟件資源的等待隊(duì)列wait_queue上,并將該進(jìn)程PCB中的狀態(tài)由R狀態(tài)設(shè)置為S狀態(tài)。那為什么要對(duì)進(jìn)程這樣做呢?
因?yàn)?strong>CPU不可能讓這個(gè)進(jìn)程在運(yùn)行隊(duì)列里什么事情都不能干,輪到這個(gè)進(jìn)程享用CPU資源都時(shí)候不能執(zhí)行,就只能一直等待某個(gè)資源就緒,這其實(shí)浪費(fèi)了時(shí)間片,浪費(fèi)了CPU的資源,所以CPU把這個(gè)進(jìn)程PCB設(shè)為S狀態(tài),從運(yùn)行隊(duì)列run_queue丟到了該外設(shè)資源的等待隊(duì)列wait_queue中去了。
當(dāng)然如果這個(gè)進(jìn)程PCB在等待隊(duì)列中等待成功了某外設(shè)資源,即某外設(shè)資源對(duì)該進(jìn)程已就緒,那就說(shuō)明這個(gè)進(jìn)程可以被CPU繼續(xù)執(zhí)行了!那此時(shí)進(jìn)程PCB的狀態(tài)就會(huì)由S狀態(tài)再設(shè)置為R狀態(tài),從外設(shè)的等待隊(duì)列wait_queue中回到CPU的運(yùn)行隊(duì)列run_queue里,繼續(xù)進(jìn)行進(jìn)程的執(zhí)行了。
這也可以幫助我們更好的理解狀態(tài)其實(shí)是一種對(duì)進(jìn)程的分類這一經(jīng)典名言。
狀態(tài)的本質(zhì)是分類,我們通過(guò)不同的隊(duì)列,其實(shí)就完成了對(duì)不同進(jìn)程的分類。一類【S狀態(tài)的PCB進(jìn)程】是需要等待某種資源就緒的,而這一類進(jìn)程都在等待隊(duì)列wait_Queue,這一類【R狀態(tài)PCB進(jìn)程】是需要去被CPU直接去執(zhí)行的,而這一列進(jìn)程都在運(yùn)行隊(duì)列run_Queue中。
2.4.3 進(jìn)程卡死與進(jìn)程等待
在舉個(gè)現(xiàn)實(shí)中的例子幫助我們理解進(jìn)程等待:
比如現(xiàn)在我打開(kāi)一個(gè)網(wǎng)易云音樂(lè),現(xiàn)在他卡死了,本質(zhì)上這是網(wǎng)易云這個(gè)進(jìn)程的問(wèn)題,是這個(gè)進(jìn)程一直沒(méi)有享用到CPU資源導(dǎo)致的,是在運(yùn)行隊(duì)列run_queue上長(zhǎng)時(shí)間卡死了嗎,不會(huì)的,縱使該進(jìn)程優(yōu)先級(jí)再低,系統(tǒng)中有平衡機(jī)制,網(wǎng)易云進(jìn)程也可以被CPU執(zhí)行到,從而執(zhí)行下去不會(huì)卡死。
那是為什么呢?這很可能就是因?yàn)檫@個(gè)進(jìn)程執(zhí)行所必需的某個(gè)資源還沒(méi)有就緒,導(dǎo)致這個(gè)進(jìn)程一直在等待這個(gè)資源而一直無(wú)法被執(zhí)行。比如說(shuō)網(wǎng)易云現(xiàn)在需要的網(wǎng)卡當(dāng)中的某些數(shù)據(jù)還沒(méi)有就緒,我這個(gè)進(jìn)程沒(méi)有讀到,網(wǎng)易云這個(gè)進(jìn)程就必須在該網(wǎng)卡資源的等待隊(duì)列wait_queue中等待該資源就緒,即此時(shí)該進(jìn)程一直處于S狀態(tài),一直在等待,啥事都不干,無(wú)法去run_queue上被CPU執(zhí)行,網(wǎng)易云這個(gè)進(jìn)程也就卡死了。
所以,卡死的本質(zhì)就是該進(jìn)程一直沒(méi)有被CPU執(zhí)行 -> 一直在等待某種外設(shè)資源(處S狀態(tài)) -> 最終我們可以看到一個(gè)彈窗:當(dāng)前xxx進(jìn)程無(wú)響應(yīng),是否讓該進(jìn)程繼續(xù)等待OR直接退出該進(jìn)程。
當(dāng)然除了這種資源條件一直不具備的情況,外設(shè)不給力,導(dǎo)致進(jìn)程PCB一直卡在等待隊(duì)列無(wú)法執(zhí)行,最終軟件卡死的情況,還有一種卡死情況,那就是某個(gè)軟硬件資源要被使用的人(進(jìn)程)太多了,大家(要使用該資源的進(jìn)程們)都擠在該資源的等待隊(duì)列wait_queue里,CPU此時(shí)可能忙著喚醒別人(別的進(jìn)程),而沒(méi)有顧及到該進(jìn)程,這個(gè)進(jìn)程沒(méi)有被調(diào)度到run_queue運(yùn)行隊(duì)列中被CPU執(zhí)行,就只能一直在等待隊(duì)列里卡死。
進(jìn)程處于R狀態(tài)的進(jìn)程,要等某種資源時(shí),進(jìn)程PCB就會(huì)從運(yùn)行隊(duì)列run_Queue中,被放到等待隊(duì)列wait_Queue中,進(jìn)程狀態(tài)就從R狀態(tài)變成S/D狀態(tài)這個(gè)過(guò)程就叫做掛起等待?。?!也叫阻塞?。?!
當(dāng)然后續(xù)進(jìn)程等待成功某種資源后,會(huì)被OS從等待隊(duì)列wait_queue放到運(yùn)行隊(duì)列run_queue當(dāng)中,(S->R),這個(gè)過(guò)程就叫做喚醒進(jìn)程?。?!
2.5 D狀態(tài)解析
2.5.1 D狀態(tài)對(duì)比S狀態(tài)
D狀態(tài)(disk sleep),可以稱之為深度睡眠狀態(tài),換句話說(shuō),D狀態(tài)是比S淺度睡眠狀態(tài)更強(qiáng)更深的睡眠等待狀態(tài)。D狀態(tài)類似于S等待掛起狀態(tài),但是我們可以說(shuō)這個(gè)D狀態(tài)下的等待更加的堅(jiān)決。
再具體說(shuō),S狀態(tài)下的等待睡眠是可以中斷的,如下面實(shí)例:
?上面的例子演示了處于S狀態(tài)正在等待某個(gè)資源就緒的進(jìn)程是可以被殺死的,但是D狀態(tài)下的等待睡眠是深度睡眠,是不可中斷的,程操作系統(tǒng)不能奈何D狀態(tài)的進(jìn)程,D狀態(tài)的進(jìn)程不能被信號(hào)喚醒,GDB等調(diào)試工具也不能對(duì)它調(diào)試,我們下面舉個(gè)生動(dòng)的例子說(shuō)明D狀態(tài)的意義。
2.5.2 生動(dòng)例子說(shuō)明D狀態(tài)意義
說(shuō)現(xiàn)在有一個(gè)外設(shè)----磁盤(pán),內(nèi)存中有一個(gè)進(jìn)程,內(nèi)存中也有一個(gè)操作系統(tǒng)OS,然后我們知道進(jìn)程讓內(nèi)存往磁盤(pán)上寫(xiě)入的時(shí)候,是一定要花時(shí)間去寫(xiě)的。磁盤(pán)往盤(pán)片上寫(xiě)數(shù)據(jù),亦是需要花時(shí)間的。磁盤(pán)在工作,即磁盤(pán)在寫(xiě)入數(shù)據(jù)的時(shí)候,你進(jìn)程只能在這邊等,你在等什么呢?等磁盤(pán)把數(shù)據(jù)寫(xiě)完,然后(磁盤(pán))告訴你(進(jìn)程)寫(xiě)完的結(jié)果。鋪墊完基本知識(shí),我們開(kāi)始故事講述:
?你是一個(gè)進(jìn)程,你站在內(nèi)存當(dāng)中,然后你這個(gè)進(jìn)程執(zhí)行到某行代碼邏輯,讓內(nèi)存中的某數(shù)據(jù)寫(xiě)入保存到磁盤(pán)當(dāng)中,數(shù)據(jù)從內(nèi)存output到磁盤(pán),磁盤(pán)大哥說(shuō):“數(shù)據(jù)來(lái)了,那好,我去幫把數(shù)據(jù)寫(xiě)到盤(pán)片上,不過(guò)需要點(diǎn)時(shí)間,你進(jìn)程給我在這等著,我寫(xiě)完告訴你成功還是失敗?!比缓?strong>磁盤(pán)就去工作了。此時(shí)進(jìn)程不能從磁盤(pán)的wait_queue折回到run_queue運(yùn)行隊(duì)列繼續(xù)執(zhí)行,而是要繼續(xù)在該磁盤(pán)的等待隊(duì)列中等待,等待磁盤(pán)寫(xiě)入完成之后,接收反饋結(jié)果。所以進(jìn)程就在這等待隊(duì)列等著,也處于S狀態(tài)(淺度休眠)了。
這個(gè)進(jìn)程S等待磁盤(pán)寫(xiě)入的過(guò)程其實(shí)挺美好的,磁盤(pán)寫(xiě)入完成,然后把寫(xiě)入結(jié)果反饋給這個(gè)進(jìn)程,然后進(jìn)程再?gòu)牡却?duì)列回到運(yùn)行隊(duì)列繼續(xù)執(zhí)行。
但是?。。‖F(xiàn)在進(jìn)程在等待的時(shí)候,OS操作系統(tǒng)路過(guò)了,OS說(shuō):“你這個(gè)進(jìn)程干嘛呢,還在這里睡眠,在這里等待什么事也不做,白白占用系統(tǒng)資源,你都不看看我OS都忙成什么樣了,現(xiàn)在內(nèi)存資源都快不夠用了?。?!”,然后操作系統(tǒng)就直接把你這個(gè)處于S等待狀態(tài)的進(jìn)程給直接干掉了(OS作為進(jìn)程的管理者當(dāng)然也有這個(gè)權(quán)利)。不過(guò)這樣問(wèn)題就大了呀,我被殺死了,那磁盤(pán)在盤(pán)片上工作完成后,磁盤(pán)把寫(xiě)入的結(jié)果反饋給誰(shuí)呢?寫(xiě)入保存成功了還好,那如果磁盤(pán)寫(xiě)入失敗了,要把寫(xiě)入失敗的消息反饋給進(jìn)程,但是現(xiàn)在進(jìn)程找不到了可怎么辦!這樣用戶就不會(huì)知道保存磁盤(pán)其實(shí)是失敗的?。?!后果很嚴(yán)重!??!
在這個(gè)過(guò)程當(dāng)中,是進(jìn)程,操作系統(tǒng),磁盤(pán)三者誰(shuí)的錯(cuò)呢,磁盤(pán)保存失敗的反饋沒(méi)有被接收,而用戶自以為寫(xiě)入成功,那這個(gè)鍋誰(shuí)背呢?
是磁盤(pán)背鍋嗎?不對(duì),磁盤(pán)只是個(gè)辦事的,而且已經(jīng)把寫(xiě)入失敗的結(jié)果信息反饋了,磁盤(pán)找不到人接收信息,怎么能怪磁盤(pán)呢。是進(jìn)程背鍋嗎?不對(duì),進(jìn)程好好的在磁盤(pán)的等待隊(duì)列里等待磁盤(pán)的反饋信息,然后就被OS干掉了,這波不能怪進(jìn)程。是OS操作系統(tǒng)背鍋嗎?也不對(duì),OS需要考慮整個(gè)系統(tǒng)資源的調(diào)配,服務(wù)于這個(gè)計(jì)算機(jī),現(xiàn)在內(nèi)存資源緊張,OS干掉沒(méi)有執(zhí)行而占用系統(tǒng)資源的進(jìn)程,這本身做的也沒(méi)錯(cuò)呀。
很顯然,這種情況下,三者其實(shí)都沒(méi)錯(cuò),但是如果在等待的S狀態(tài)進(jìn)程被OS不小心干掉了,而導(dǎo)致數(shù)據(jù)丟失,但是其實(shí)誰(shuí)都沒(méi)有問(wèn)題,這種我們假設(shè)的情況我們能允許它存在嗎?當(dāng)然不允許!?。?/strong>這件事的根本在于進(jìn)程等待的時(shí)候,被操作系統(tǒng)誤殺,那能不能不讓這個(gè)進(jìn)程在等待的時(shí)候被殺掉呢。操作系統(tǒng)考慮到了這一點(diǎn)?。。?/strong>
如果進(jìn)程處于S等待狀態(tài)(淺度睡眠),在等待時(shí)就可以被OS殺死,而如果進(jìn)程處于D狀態(tài)(深度睡眠),此時(shí)就不會(huì)該進(jìn)程就不會(huì)被OS干掉。這就是D狀態(tài)的意義,只要你這個(gè)進(jìn)程處于D等待睡眠狀態(tài),OS就不能干掉你。
故接下來(lái)故事反轉(zhuǎn):當(dāng)你這個(gè)進(jìn)程是處于D狀態(tài),在等待隊(duì)列wait_queue中不可中斷的等待磁盤(pán),那如果此時(shí)OS路過(guò),想要?dú)⑺滥氵@個(gè)什么都不干,就在睡眠而卻占有系統(tǒng)資源的進(jìn)程,此時(shí)你這個(gè)進(jìn)程默默掏出了D狀態(tài)深度睡眠狀態(tài),那OS就不能殺死你了!就不能殺死這個(gè)D進(jìn)程,D進(jìn)程就是一個(gè)爺,OS就只得去殺別的在等待的進(jìn)程了。
然后D狀態(tài)的這個(gè)進(jìn)程就安然無(wú)恙的等待磁盤(pán)寫(xiě)入,等待接收磁盤(pán)的反饋信息,等磁盤(pán)就緒時(shí),該進(jìn)程就會(huì)被喚醒,攜帶反饋信息回到運(yùn)行隊(duì)列,被CPU繼續(xù)執(zhí)行,然后進(jìn)程就可以將這個(gè)接收到的這個(gè)磁盤(pán)寫(xiě)入結(jié)果的信息反饋到上層。Perfect!!!
2.5.3 D狀態(tài)進(jìn)程問(wèn)題補(bǔ)充
D狀態(tài)的進(jìn)程也會(huì)出現(xiàn)一個(gè)問(wèn)題:如果系統(tǒng)中存在大量的D狀態(tài)進(jìn)程正在占有系統(tǒng)資源,由于甚至連OS都不能殺死D狀態(tài)的進(jìn)程,現(xiàn)在的對(duì)策就只有等待D進(jìn)程自己執(zhí)行完畢或者直接重啟機(jī)器,否則沒(méi)有任何方法處理掉D狀態(tài)的進(jìn)程,所以系統(tǒng)資源就會(huì)被大量占有,出現(xiàn)卡頓宕機(jī)的情況。
2.6 T狀態(tài)介紹
T狀態(tài),暫停狀態(tài),英文名stopped。
T狀態(tài)進(jìn)程需求:執(zhí)行某些任務(wù)的時(shí)候,這個(gè)進(jìn)程需要暫停執(zhí)行。
暫停不是睡眠等待,縱使S等待狀態(tài)和T暫停狀態(tài)有一點(diǎn)像,但其實(shí)根本上是不同的。他們的相同在于都不會(huì)被CPU執(zhí)行。S狀態(tài)是在等待隊(duì)列wait_queue中等待某個(gè)外設(shè)資源,沒(méi)有在run_queue中,不能接受到CPU的資源。而T狀態(tài)是直接讓這個(gè)進(jìn)程暫停,CPU無(wú)法執(zhí)行這個(gè)已經(jīng)暫停的進(jìn)程。如果說(shuō)處于T狀態(tài)的進(jìn)程感受到的是時(shí)間暫停,那S狀態(tài)下的進(jìn)程感受到的則是時(shí)光流逝中無(wú)窮等待的感覺(jué),這兩類進(jìn)程都不能去做事(被CPU執(zhí)行)。
進(jìn)程處于S狀態(tài)(sleeping睡眠)是代碼沒(méi)有跑,什么事都沒(méi)干,但是核心數(shù)據(jù)會(huì)被更新改變。比如你這個(gè)進(jìn)程執(zhí)行sleep語(yǔ)句時(shí),sleep休眠多少時(shí)間了,這個(gè)數(shù)據(jù)會(huì)被更新,進(jìn)程是真的在等待某件事情的到來(lái),這個(gè)等待是有意義的,這個(gè)等待在某種意義上也是一種運(yùn)行。而T狀態(tài)則是徹底的中斷,暫停!你壓根不會(huì)有任何的數(shù)據(jù)更新?。。?/strong>
2.7 t狀態(tài)介紹
t狀態(tài),跟蹤狀態(tài),英文名tracing stop。
事實(shí)上,追蹤也是一種暫停態(tài),比如進(jìn)程在代碼的某一行打了一個(gè)斷點(diǎn),運(yùn)行就在斷點(diǎn)處停下來(lái)了,這就是典型的t狀態(tài)。即因?yàn)樽粉櫠幱诘臅和顟B(tài)(t狀態(tài)),當(dāng)然此時(shí)t狀態(tài)的進(jìn)程也方便你查看數(shù)據(jù)。
2.8 X狀態(tài)介紹
X狀態(tài),死亡狀態(tài),英文名dead。
進(jìn)程死亡,說(shuō)白一點(diǎn),就是就等同于進(jìn)程資源被回收。什么是進(jìn)程資源的回收呢?我們知道創(chuàng)建進(jìn)程的時(shí)候,做了什么動(dòng)作,在回收的時(shí)候肯定要做相反的動(dòng)作,所以:
回收進(jìn)程資源 == 即回收進(jìn)程相關(guān)的數(shù)據(jù)結(jié)構(gòu)(task_struct)?+ 你的代碼和數(shù)據(jù)(進(jìn)程本體)。
進(jìn)程X死亡,就是干干凈凈,什么都沒(méi)有了?。?!
?所以進(jìn)程的X死亡狀態(tài)很少是很少能被我們獲悉觀察到,因?yàn)檫M(jìn)程dead死亡,就意味著進(jìn)程資源的釋放,沒(méi)有進(jìn)程的資源你也很難觀察到X死亡狀態(tài)了。因?yàn)?strong>狀態(tài)也是一種進(jìn)程的屬性數(shù)據(jù),也是在PCB中保存的,進(jìn)程的PCB數(shù)據(jù)結(jié)構(gòu)和代碼數(shù)據(jù)資源都被釋放完畢了,存儲(chǔ)在PCB的X狀態(tài)也就沒(méi)有了,我們也很難查看到X狀態(tài)了。
進(jìn)程退出死亡的全部過(guò)程:首先變成Z狀態(tài),最后再變成X狀態(tài),先僵尸,再死亡,進(jìn)程首先退出,并不會(huì)回收所有該進(jìn)程的資源,而是例如進(jìn)程會(huì)先保留該進(jìn)程的退出信息,進(jìn)程進(jìn)入僵尸Z狀態(tài),而當(dāng)所有該進(jìn)程的資源被回收之后,進(jìn)程會(huì)進(jìn)入X死亡狀態(tài)。
2.9 Z狀態(tài)詳解
Z狀態(tài),僵尸狀態(tài),英文名zombie。
如何理解僵尸狀態(tài),我們先舉一個(gè)生動(dòng)的例子類比一下吧。
2.9.1 故事講解
你作為一個(gè)熱愛(ài)運(yùn)動(dòng)的優(yōu)秀大學(xué)生,是十分喜歡晨跑的人,這天你正在晨跑,突然你發(fā)現(xiàn)有一個(gè)人從你旁邊呼嘯而過(guò),他如風(fēng)一般,但是這個(gè)風(fēng)一樣的男子在跑出你20m后突然倒下了,等你反應(yīng)過(guò)去去看的時(shí)候,發(fā)現(xiàn)這個(gè)人其實(shí)已經(jīng)失去了生命體征,大概率是某種惡性基本如心肌梗塞,腦溢血等疾病的突發(fā),總之說(shuō)明這個(gè)人只剩下遺體了(尊重死者),你雖然不會(huì)急救知識(shí),但是你還是一個(gè)有良知的人,你會(huì)去做一件事----打電話,聯(lián)系110,當(dāng)然110說(shuō)他們趕到的時(shí)候一定會(huì)隨叫到120,所以你只打了110。等到110警察到達(dá)之后,警察要做的第一件事情,并不是立即把這個(gè)人的遺體處理掉,而是首先全面封鎖現(xiàn)場(chǎng),然后叫法醫(yī)過(guò)來(lái)進(jìn)行鑒定,這才是110首先一個(gè)干的事情,等待法醫(yī)鑒定完畢才會(huì)通知后事部門(mén)回收遺體。
在這個(gè)現(xiàn)實(shí)中的例子中有兩件事。
為什么這個(gè)人已死,只剩下遺體,警察不立即回收走呢,而是要封鎖現(xiàn)場(chǎng),然后叫法醫(yī)過(guò)來(lái)鑒定呢?因?yàn)?strong>警察要調(diào)查出一件事,這個(gè)人是因?yàn)榧膊∵€是他殺什么原因,即死因。調(diào)查死因這是第一件事。(調(diào)查死因的時(shí)候,遺體一直在那里躺著)
然后法醫(yī)鑒定完畢之后,警察會(huì)不會(huì)直接撤走封鎖線,直接說(shuō)任務(wù)完成,就直接走了,把遺體放在風(fēng)中凌亂呢?當(dāng)然也不會(huì),警察當(dāng)然是要通知后事部門(mén),把遺體處理好后,才會(huì)收隊(duì)撤離?;厥者z體這是第二件事。(在調(diào)查出死因之后,遺體才會(huì)回收)
當(dāng)調(diào)查死因結(jié)束,警察做完再通知后事部門(mén)處理完遺體后,這件事才是真正結(jié)束了。
2.9.2 事例類比進(jìn)程
在這里可以類比進(jìn)程退出死亡的全程,警察在法醫(yī)鑒定完成之后,通知后事部門(mén)把這個(gè)遺體真正回收完畢,這個(gè)人這件事才真正結(jié)束,類比進(jìn)程就是這個(gè)進(jìn)程才真正進(jìn)入了X死亡狀態(tài),而之前法醫(yī)調(diào)查死因,死者的遺體一直躺在地上的這段時(shí)間,類比進(jìn)程就是這個(gè)進(jìn)程處于的就是Z僵尸狀態(tài)。
為什么警察來(lái)了之后不是立即回收遺體,而是先讓遺體躺著呢,等法醫(yī)鑒定完畢再做呢?
因?yàn)?strong>警察需要辨別死者的死亡原因!?。?/strong>確定這個(gè)人是自殺,疾病還是他殺等。這是因?yàn)槲覀兇_定一個(gè)人死亡的原因是為了給社會(huì)一個(gè)交代,如果一個(gè)人的死因沒(méi)有調(diào)查清楚,可能會(huì)引起社會(huì)恐慌,而且死因沒(méi)有調(diào)查清楚,媒體會(huì)如何報(bào)道,輿情會(huì)如何發(fā)展,這些問(wèn)題都非常難以處理。所以我們必須要調(diào)查遺體死因,從而給社會(huì)一個(gè)交代!??!
這個(gè)可以類比進(jìn)程為什么要首先處于Z僵尸狀態(tài),而不是直接進(jìn)入X死亡狀態(tài)?
首先我們創(chuàng)建出一個(gè)進(jìn)程(子進(jìn)程),其目的是為了讓這個(gè)進(jìn)程去完成某種任務(wù),那這個(gè)任務(wù)最終被這個(gè)進(jìn)程完成的如何呢?你這個(gè)進(jìn)程是把任務(wù)完成正常退出,還是中間不小心被OS干掉了,還是因?yàn)檫M(jìn)程自己代碼有問(wèn)題而進(jìn)程退出,這些都要給父進(jìn)程要對(duì)系統(tǒng)有一個(gè)交代?。?!
遺體要在地上躺一會(huì),被法醫(yī)鑒定原因,然后才會(huì)回收遺體,這個(gè)遺體躺著的過(guò)程類比進(jìn)程就是,進(jìn)程處于Z狀態(tài)。繼續(xù)類比,其實(shí)進(jìn)程退出的時(shí)候,并不會(huì)回收掉一切資源,也即不會(huì)直接進(jìn)入X死亡狀態(tài),退出的進(jìn)程而是會(huì)暫存該進(jìn)程的退出信息,其實(shí)這個(gè)退出信息就可以類比例子中暫不被回收的遺體。? ? ? ??
那這個(gè)進(jìn)程的退出信息暫存在哪里呢?進(jìn)程的退出信息也是數(shù)據(jù),這個(gè)數(shù)據(jù)跟進(jìn)程強(qiáng)相關(guān),那肯定就是存放在這個(gè)進(jìn)程的PCB task_struct中。即當(dāng)一個(gè)進(jìn)程退出的時(shí)候,它的所有資源并不是會(huì)立即釋放的,而是首先讓這個(gè)進(jìn)程進(jìn)入僵尸,把這個(gè)進(jìn)程的退出信息存在這個(gè)進(jìn)程的PCB中,來(lái)供我們的父進(jìn)程或系統(tǒng)去讀取,調(diào)查死因之后(讀取退出信息后)才會(huì)退出。
這仿佛遺體在說(shuō):"我還不能被回收(進(jìn)程X回收所有資源),我遺體還要躺一會(huì)(先進(jìn)入Z狀態(tài) 退出信息保存到PCB),我的死因還沒(méi)有調(diào)查清楚!"
總之,一個(gè)進(jìn)程的退出并不會(huì)立即進(jìn)入X狀態(tài)回收所有進(jìn)程資源,而是會(huì)首先進(jìn)入Z狀態(tài)保存退出信息到PCB。等到其退出信息被父進(jìn)程/系統(tǒng)讀取后,所有的進(jìn)程資源被父進(jìn)程/系統(tǒng)回收,該進(jìn)程才進(jìn)入X狀態(tài)。
實(shí)際上,進(jìn)程退出之后,它的退出狀態(tài),退出碼等退出信息都是會(huì)寫(xiě)在這個(gè)進(jìn)程的task_struct中的,進(jìn)程的僵尸狀態(tài),就是進(jìn)程的退出信息保存在PCB中,進(jìn)程的部分資源仍然存在,所以僵尸資源就會(huì)占用系統(tǒng)資源,這樣就會(huì)導(dǎo)致內(nèi)存泄漏等問(wèn)題。我們會(huì)在下篇博客中介紹如何解決僵尸狀態(tài),以及退出信息退出碼。
2.10 進(jìn)程狀態(tài)總結(jié)
就緒態(tài)就是進(jìn)程在run_Queue運(yùn)行隊(duì)列里等待CPU資源;運(yùn)行態(tài)就是CPU的時(shí)間片到來(lái)運(yùn)行該進(jìn)程。就緒態(tài)+運(yùn)行態(tài)在Linux下是R狀態(tài)。
掛起等待/阻塞狀態(tài) 就是 S , D , T狀態(tài),一般來(lái)說(shuō)是S/D狀態(tài),S,D狀態(tài)的進(jìn)程,在某資源的等待隊(duì)列wait_Queue中等待該資源就緒,T狀態(tài)的進(jìn)程就是處于完全的暫停狀態(tài)。
3. 在實(shí)踐中驗(yàn)證各個(gè)狀態(tài)
我們主要進(jìn)行對(duì)于R,S,T狀態(tài)的驗(yàn)證,D狀態(tài)進(jìn)程的實(shí)現(xiàn)需要涉及到讀盤(pán)寫(xiě)盤(pán)(這樣才會(huì)產(chǎn)生等待卻不可以被OS殺死的進(jìn)程),會(huì)對(duì)云服務(wù)器產(chǎn)生損害,所以我們不驗(yàn)證。
3.1 R狀態(tài)驗(yàn)證
?也就是說(shuō)這個(gè)Myproc進(jìn)程一直在進(jìn)行死循環(huán)一直在run_Queue上等待/享用CPU的資源。
3.2 S狀態(tài)驗(yàn)證
3.2.1 S狀態(tài)簡(jiǎn)單示例
進(jìn)程在等待鍵盤(pán)這個(gè)外設(shè)資源(輸入設(shè)備)的數(shù)據(jù)時(shí),在鍵盤(pán)的等待隊(duì)列wait_Queue中等待,處于的就是S狀態(tài)。
3.2.2 S狀態(tài)復(fù)雜示例
此時(shí)我們驚奇的發(fā)現(xiàn),這個(gè)進(jìn)程明明執(zhí)行的邏輯是一直cout打印hello world,應(yīng)該是一直在執(zhí)行,一直處于R狀態(tài)呀,而且我都打印出來(lái)了,肯定也在CPU上執(zhí)行了呀,那為什么會(huì)出現(xiàn)檢測(cè)到該進(jìn)程一直處于S狀態(tài)呢?
首先我們明白,S狀態(tài)表面這個(gè)進(jìn)程在等待某個(gè)外設(shè)資源,而這個(gè)進(jìn)程要執(zhí)行的邏輯就是打印,能涉及到的外設(shè)就是顯示器,顯示器也不是一直可以被這個(gè)特定的進(jìn)程使用的,所以這個(gè)進(jìn)程處于S狀態(tài),是在顯示器的wait_Queue等待隊(duì)列當(dāng)中。而這個(gè)確實(shí)執(zhí)行了打印邏輯,所以肯定在某些個(gè)時(shí)刻,是處于R態(tài),享受CPU資源或處在運(yùn)行隊(duì)列當(dāng)中的。
我們可以確定,這個(gè)進(jìn)程既有R態(tài),也有S態(tài),那為什么檢測(cè)出來(lái)的都是S態(tài)呢?這肯定是因?yàn)檫@個(gè)進(jìn)程的絕大部分時(shí)間都是處于S狀態(tài),所以我們監(jiān)測(cè)到的也基本上是S狀態(tài),R狀態(tài)很難被捕捉,那為什么絕大部分時(shí)間是處于S狀態(tài)而不是R狀態(tài)呢?
cout<<"hello world"<<endl , 這個(gè)語(yǔ)句是在進(jìn)行到屏幕上的打印,是從內(nèi)存打印到外設(shè)(輸出設(shè)備)output的過(guò)程,也即I/O過(guò)程,IO從內(nèi)存到外設(shè),這個(gè)過(guò)程的效率是很慢的!
?所以進(jìn)程每做一次打印,打印的數(shù)據(jù)就要經(jīng)歷從內(nèi)存到外設(shè)經(jīng)過(guò)漫長(zhǎng)的IO。同時(shí),顯示器也不是每時(shí)每刻都能被打印的,進(jìn)程需要等待每一次輸出設(shè)備--顯示器的就緒,所以其實(shí)進(jìn)程等待的時(shí)間是遠(yuǎn)長(zhǎng)于進(jìn)程在CPU上執(zhí)行打印的時(shí)間的。
在追求效率以及速度極快的CPU看來(lái),你等待硬件資源的時(shí)間太長(zhǎng)了,會(huì)損耗我CPU的運(yùn)行效率,所以CPU就把你這個(gè)進(jìn)程的PCB從運(yùn)行隊(duì)列放到等待隊(duì)列里去了,也就是從R狀態(tài)->S狀態(tài),然后長(zhǎng)久的處于S狀態(tài),顯示器就緒后再短暫的回到運(yùn)行隊(duì)列R執(zhí)行,之后再回來(lái),周而復(fù)始。這才是較為完整的邏輯。
所以其實(shí)這個(gè)進(jìn)程并不是用戶感官上認(rèn)為的那樣,這個(gè)進(jìn)程并不是一直在R運(yùn)行,而是其實(shí),大部分時(shí)間都處于S(sleeping 休眠)等待狀態(tài),等待顯示器資源的就緒,畢竟打印需要顯示器資源就緒,就需要IO,而IO是非常非常慢的,實(shí)際R運(yùn)行的時(shí)間是非常少的。
我們?cè)賹?duì)比一下,加深一下理解:
while(true);? ? vs? ? ?while(true){cout<<hello world;}
Test1:while(true); ->
Test2:while(true){cout<<hello world;} ->
?前者while(true);死循環(huán)執(zhí)行,是不需要等待什么資源的,可以直接被CPU執(zhí)行,所以也就是說(shuō),會(huì)一直在運(yùn)行隊(duì)列run_Queue中,一直處于R狀態(tài)。
而后者 while(true){cout<<hello world} ,需要等待顯示器資源就緒,才能完成打印,其大部分時(shí)間不在CPU執(zhí)行,而是在顯示器資源的等待隊(duì)列wait_Queue里等待,所以大部分時(shí)間都是處于S狀態(tài),而只有小部分時(shí)間處于R狀態(tài)。
3.3 T暫停狀態(tài)演示
3.3.1 T狀態(tài)進(jìn)程演示
T狀態(tài),是進(jìn)程直接暫停的狀態(tài),任何數(shù)據(jù)不會(huì)再有改變,在現(xiàn)實(shí)中看就像時(shí)間暫停一般。那我們?nèi)绾沃袛鄷和R粋€(gè)正在運(yùn)行的進(jìn)程呢?
我們要通過(guò)使用信號(hào)的方式,來(lái)暫停進(jìn)程 ,當(dāng)然具體信號(hào)的概念我們?cè)诤竺娴牟┛椭v,我們首先講操作系統(tǒng)中信號(hào)的使用。
?所以我們可以通過(guò)指令 kill? -19? PID ,??就可以暫停PID進(jìn)程,從而該進(jìn)程中斷執(zhí)行,該進(jìn)程就變成了T狀態(tài)。下面我們做實(shí)驗(yàn):
?關(guān)于前臺(tái)進(jìn)程,后臺(tái)進(jìn)程,這對(duì)概念我們?cè)诤竺嬷v述,我們這里主要演示T狀態(tài)。
下面我們演示18號(hào)信號(hào),SIGCONT,可以讓中斷暫停的進(jìn)程continue繼續(xù)跑起來(lái)。
對(duì)處于R+狀態(tài)的進(jìn)程發(fā)送19號(hào)信號(hào)SIGSTOP,將進(jìn)程暫停,會(huì)將這個(gè)進(jìn)程由前臺(tái)運(yùn)行變成后臺(tái)運(yùn)行。
我們可以發(fā)現(xiàn)一個(gè)現(xiàn)象,無(wú)論進(jìn)程現(xiàn)在處于T狀態(tài),還是R狀態(tài),這些不帶+號(hào)的后臺(tái)進(jìn)程(當(dāng)然排除普通的R+狀態(tài) 帶+號(hào)表示前臺(tái)進(jìn)程),現(xiàn)在?我們Ctrl+C想刪除這個(gè)myproc進(jìn)程是沒(méi)有辦法的,這其實(shí)是后臺(tái)進(jìn)程的特性。
3.3.2 前臺(tái)運(yùn)行&&后臺(tái)運(yùn)行
在系統(tǒng)中運(yùn)行進(jìn)程,進(jìn)程可以在前臺(tái)運(yùn)行,也可以在后臺(tái)運(yùn)行,我們平時(shí)在命令行上執(zhí)行的程序,fork創(chuàng)建的子進(jìn)程,都是讓進(jìn)程在前臺(tái)運(yùn)行。那如何讓進(jìn)程在后臺(tái)運(yùn)行呢?前臺(tái)進(jìn)程和后臺(tái)進(jìn)程有什么區(qū)別呢?
讓程序在前臺(tái)運(yùn)行:平時(shí)直接在命令行上敲./proc 執(zhí)行起來(lái)的程序,后續(xù)就是前臺(tái)運(yùn)行的進(jìn)程。
前臺(tái)運(yùn)行的特點(diǎn):
1.在前臺(tái)執(zhí)行的進(jìn)程可以被 ps axj 指令監(jiān)控到是帶+號(hào)的狀態(tài)的(如R+)。
2.我們Ctrl+C是可以直接殺死這個(gè)進(jìn)程的。當(dāng)然kill -9 PID也可以殺死。
3.在前臺(tái)進(jìn)程執(zhí)行的時(shí)候是無(wú)法執(zhí)行l(wèi)s,pwd等指令的。
讓程序在后臺(tái)運(yùn)行:除了剛才給運(yùn)行的進(jìn)程發(fā)19 18號(hào)信號(hào),讓進(jìn)程變?yōu)楹笈_(tái)進(jìn)程,我們通常是在執(zhí)行程序時(shí)在后面帶一個(gè)&號(hào),使該進(jìn)程在后臺(tái)運(yùn)行。
后臺(tái)運(yùn)行的特點(diǎn):
1.在前臺(tái)執(zhí)行的進(jìn)程可以被 ps axj 指令監(jiān)控到是不帶帶+號(hào)的狀態(tài)的(如R,T等)。
2.我們Ctrl+C是可以直接殺死這個(gè)進(jìn)程的,只能通過(guò)強(qiáng)大的 kill -9 PID指令殺死。
3.?后臺(tái)運(yùn)行的進(jìn)程,在命令行上輸入ls pwd mkdir是可以直接執(zhí)行的。
3.4* Z僵尸狀態(tài)演示
3.4.1 Z狀態(tài)進(jìn)程的回收
如果一個(gè)進(jìn)程運(yùn)行完畢要退出了,這個(gè)進(jìn)程就要保持住自己的退出信息到PCB中,以供父進(jìn)程/系統(tǒng)去讀取,而不會(huì)回收進(jìn)程的全部資源。這個(gè)進(jìn)程要退出,而資源沒(méi)有被回收的過(guò)程,進(jìn)程所處的狀態(tài)就是Z僵尸狀態(tài)。
回到我們剛剛調(diào)查死因的例子,如果這個(gè)人的遺體一直在風(fēng)中凌亂,沒(méi)有被警察法醫(yī)調(diào)查完死因,同樣此時(shí)后事部門(mén)沒(méi)有被警察通知,所以也就沒(méi)有后事部門(mén)去回收,那其實(shí)死者的遺體就會(huì)一直在風(fēng)中凌亂,這其實(shí)在現(xiàn)實(shí)中影響是非常惡劣的。
同樣的類比,如果這個(gè)進(jìn)程處于Z狀態(tài),即此時(shí)進(jìn)程還有沒(méi)有被回收完畢的退出信息資源。如果這個(gè)進(jìn)程的的退出信息資源一直沒(méi)有被父進(jìn)程/系統(tǒng)讀取到,同時(shí)資源也就沒(méi)有被回收,那就會(huì)一直處于Z狀態(tài),一直占用系統(tǒng)資源。
故死者的遺體需要被警察知道,然后二板斧:警察組織調(diào)查死因,警察通知后事部門(mén)回收。
要退出的Z進(jìn)程也需要被知道,然后被組織讀取退出信息,并被回收。
死者的遺體由警察去負(fù)責(zé),那負(fù)責(zé)這個(gè)進(jìn)程的退出信息讀取和回收任務(wù)的是誰(shuí)呢?
這當(dāng)然是這個(gè)進(jìn)程(子進(jìn)程)的父進(jìn)程?。?!
所以子進(jìn)程在退出,退出信息資源保存在系統(tǒng)中,處于Z狀態(tài)的時(shí)候,這時(shí)候就需要父進(jìn)程來(lái)進(jìn)行對(duì)子進(jìn)程退出信息的讀取,以及由父進(jìn)程讀取退出信息后的回收該進(jìn)程的僵尸資源,之后這個(gè)進(jìn)程就可以由Z狀態(tài)變X狀態(tài),最終死亡。
當(dāng)然如果父進(jìn)程一直沒(méi)有去讀取退出信息,一直沒(méi)有回收這個(gè)子進(jìn)程的僵尸資源,那這個(gè)子進(jìn)程就會(huì)一直處于Z狀態(tài),一直占用系統(tǒng)資源。我們?cè)诤竺娌┛蜁?huì)講,父進(jìn)程可以通過(guò)進(jìn)程等待的方式來(lái)對(duì)子進(jìn)程進(jìn)行退出讀取以及資源回收。
所以接下來(lái)我們就從父子進(jìn)程這重關(guān)系入手,來(lái)看子進(jìn)程的Z狀態(tài)。
3.4.2 Z狀態(tài)驗(yàn)證1
如果我們父進(jìn)程和子進(jìn)程在運(yùn)行的時(shí)候,突然殺死子進(jìn)程,要讓子進(jìn)程退出,此時(shí)會(huì)發(fā)生什么呢?
根據(jù)之前的理論,遺體不會(huì)被立即回收,而是要首先被警察知道,然后警察組織調(diào)查死因&&之后后事部門(mén)的通知。
子進(jìn)程當(dāng)然會(huì)保存自己的退出信息,處于Z狀態(tài),等待父進(jìn)程對(duì)退出信息的讀取&&子進(jìn)程資源的回收。下面我們看具體演示:
?上面我們是在父子進(jìn)程在運(yùn)行的生活,我們首先殺死子進(jìn)程,對(duì)Z僵尸狀態(tài)的驗(yàn)證。那如果我們首先殺死父進(jìn)程呢?
?3.4.2 Z狀態(tài)驗(yàn)證2
如果讓父進(jìn)程先于子進(jìn)程退出,那子進(jìn)程就會(huì)沒(méi)有父進(jìn)程了!這其實(shí)是非常嚴(yán)重的事情,因?yàn)?strong>子進(jìn)程的退出信息需要父進(jìn)程讀取,子進(jìn)程的Z僵尸資源需要被父進(jìn)程回收!如果沒(méi)有父進(jìn)程的話,那這個(gè)被“拋棄”的子進(jìn)程的僵尸資源就無(wú)法被回收,就會(huì)一直占用系統(tǒng)資源!其實(shí)這個(gè)被父進(jìn)程“拋棄”的子進(jìn)程有個(gè)特定稱謂----孤兒進(jìn)程。
在現(xiàn)實(shí)中,當(dāng)一個(gè)小孩紙被父母拋棄的時(shí)候,會(huì)有好心人來(lái)領(lǐng)走撫養(yǎng)。在Linux操作系統(tǒng)當(dāng)中,當(dāng)父進(jìn)程先于子進(jìn)程退出時(shí),對(duì)該類子進(jìn)程會(huì)有一個(gè)類似的解決方案,那就是領(lǐng)養(yǎng)。
當(dāng)子進(jìn)程的父進(jìn)程先于子進(jìn)程退出時(shí),子進(jìn)程會(huì)被1號(hào)進(jìn)程(PID==1)領(lǐng)養(yǎng),1號(hào)進(jìn)程成為該子進(jìn)程的新父進(jìn)程,這個(gè)1號(hào)進(jìn)程其實(shí)就是操作系統(tǒng)。所以當(dāng)這個(gè)子進(jìn)程運(yùn)行結(jié)束退出的時(shí)候,就由1號(hào)進(jìn)程來(lái)讀取退出信息,由1號(hào)進(jìn)程來(lái)回收它的僵尸資源~文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-842236.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-842236.html
到了這里,關(guān)于[入門(mén)篇]Linux操作系統(tǒng)fork子進(jìn)程的創(chuàng)建以及進(jìn)程的狀態(tài) 超超超詳解!?。∥也辉试S有人錯(cuò)過(guò)?。?!的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!