目錄
1.進(jìn)程排隊(duì)
時(shí)間片
時(shí)間片的分配
結(jié)構(gòu)體內(nèi)存對(duì)齊
偏移量補(bǔ)充
對(duì)齊規(guī)則?
為什么會(huì)有對(duì)齊
2.操作系統(tǒng)學(xué)科層面對(duì)進(jìn)程狀態(tài)的理解
2.1進(jìn)程的狀態(tài)理解
①我們說所謂的狀態(tài)就是一個(gè)整型變量,是task_struct中的一個(gè)整型變量
②.狀態(tài)決定了接下來的動(dòng)作
2.2運(yùn)行狀態(tài)
2.3 阻塞狀態(tài)?
2.4 掛起狀態(tài)
3.linux下具體的進(jìn)程狀態(tài)(重要)
3.1 R和s?
(top)命令
?+:前后臺(tái)程序
3.2 休眠狀態(tài)s
3.3 disk sleep? ? ?不可中斷睡眠也叫作深度睡眠。
3.4 T狀態(tài):進(jìn)程變成暫停狀態(tài)
3.5 t狀態(tài)
3.6 z狀態(tài):僵尸狀態(tài)
4. 孤兒進(jìn)程
5.結(jié)語
1.進(jìn)程排隊(duì)
狹義上講,進(jìn)程 = task_struct + 可執(zhí)行程序。
①當(dāng)一個(gè)進(jìn)程加載到內(nèi)存的時(shí)候,首先這個(gè)進(jìn)程不是一直在內(nèi)存中運(yùn)行的也不是一直在運(yùn)行的。我們啟動(dòng)一個(gè)軟件,并不是說我們啟動(dòng)這個(gè)軟件,他就是一直被運(yùn)行的,他可能因?yàn)橐却撤N資源加載而處于某種等待狀態(tài)。
等待鍵盤資源輸入,所以進(jìn)程卡住等待我們輸入:
②即使進(jìn)程被cpu調(diào)度,也不是一直在運(yùn)行的。
?當(dāng)我們寫一個(gè)while死循環(huán)的程序,交給cpu去運(yùn)行,cpu可能被打滿,如果cpu只是執(zhí)行我們這個(gè)程序,那么就意味著這個(gè)程序會(huì)一直在cpu上運(yùn)行,沒有結(jié)束就從cpu上下不來,其他進(jìn)程不可能被調(diào)度,那么其他所有進(jìn)程跑不了,但是事實(shí)是:可能此時(shí)會(huì)卡,但是其他進(jìn)程還是正常運(yùn)行,這說明進(jìn)程不是放在cpu就要等執(zhí)行完才下來,當(dāng)代計(jì)算機(jī)都支持一個(gè)時(shí)間片的概念,cpu會(huì)給你這個(gè)程序一個(gè)運(yùn)行時(shí)間,你上來就跑這么長時(shí)間,跑完就走,還要讓其他進(jìn)程執(zhí)行,比如這個(gè)時(shí)間為1毫秒,1毫秒就是時(shí)間片.
時(shí)間片
時(shí)間片(timeslice)又稱為“量子(quantum)”或“處理器片(processor slice)”是分時(shí)操作系統(tǒng)分配給每個(gè)正在運(yùn)行的進(jìn)程微觀上的一段CPU時(shí)間(在搶占內(nèi)核中是:從進(jìn)程開始運(yùn)行直到被搶占的時(shí)間)。現(xiàn)代操作系統(tǒng)(如:Windows、Linux、Mac OS X等)允許同時(shí)運(yùn)行多個(gè)進(jìn)程 —— 例如,你可以在打開音樂播放器聽音樂的同時(shí)用瀏覽器瀏覽網(wǎng)頁并下載文件。事實(shí)上,雖然一臺(tái)計(jì)算機(jī)通常可能有多個(gè)CPU,但是同一個(gè)CPU永遠(yuǎn)不可能真正地同時(shí)運(yùn)行多個(gè)任務(wù)。在只考慮一個(gè)CPU的情況下,這些進(jìn)程“看起來像”同時(shí)運(yùn)行的,實(shí)則是輪番穿插地運(yùn)行,由于時(shí)間片通常很短(在Linux上為5ms-800ms),用戶不會(huì)感覺到。
時(shí)間片由操作系統(tǒng)內(nèi)核的調(diào)度程序分配給每個(gè)進(jìn)程。首先,內(nèi)核會(huì)給每個(gè)進(jìn)程分配相等的初始時(shí)間片,然后每個(gè)進(jìn)程輪番地執(zhí)行相應(yīng)的時(shí)間,當(dāng)所有進(jìn)程都處于時(shí)間片耗盡的狀態(tài)時(shí),內(nèi)核會(huì)重新為每個(gè)進(jìn)程計(jì)算并分配時(shí)間片,如此往復(fù)。
時(shí)間片的分配
通常狀況下,一個(gè)系統(tǒng)中所有的進(jìn)程被分配到的時(shí)間片長短并不是相等的,盡管初始時(shí)間片基本相等(在Linux系統(tǒng)中,初始時(shí)間片也不相等,而是各自父進(jìn)程的一半),系統(tǒng)通過測量進(jìn)程處于“睡眠”和“正在運(yùn)行”狀態(tài)的時(shí)間長短來計(jì)算每個(gè)進(jìn)程的交互性,交互性和每個(gè)進(jìn)程預(yù)設(shè)的靜態(tài)優(yōu)先級(jí)(Nice值)的疊加即是動(dòng)態(tài)優(yōu)先級(jí),動(dòng)態(tài)優(yōu)先級(jí)按比例縮放就是要分配給那個(gè)進(jìn)程時(shí)間片的長短。一般地,為了獲得較快的響應(yīng)速度,交互性強(qiáng)的進(jìn)程(即趨向于IO消耗型)被分配到的時(shí)間片要長于交互性弱的(趨向于處理器消耗型)進(jìn)程。(引自百度百科)
所謂進(jìn)程排隊(duì),一定是為了等待某種資,可以包括cpu,鍵盤、磁盤、等等...........
而內(nèi)存中有很多的進(jìn)程隊(duì)列,進(jìn)程排隊(duì)是經(jīng)常的進(jìn)程描述塊在排隊(duì)。就比如我們?nèi)フ夜ぷ鲿r(shí),投遞簡歷,簡歷在hr處排隊(duì),拿到誰的簡歷了,就讓相應(yīng)的人來,是簡歷在排隊(duì)。而簡歷就可以說是描述人的工作屬性的一個(gè)結(jié)構(gòu)體或者類。。所以只要是排隊(duì),一定是進(jìn)程的task_struct在排隊(duì)。?而一個(gè)task_struct可以被鏈入多種數(shù)據(jù)結(jié)構(gòu),linux中是被鏈入雙鏈表中的,linux實(shí)現(xiàn)雙鏈表的方式和我們c語言中是不大一樣的:
首先在pcb中定義了這樣一個(gè)結(jié)構(gòu)體:
struct listnode
{
struct listnode *next;
struct listnode *prev;
};
在每個(gè)進(jìn)程的pcb中
我們通過鏈?zhǔn)綄?duì)象找到listnode對(duì)象,那么還有很多的屬性怎么訪問呢。原理
struct listnode 是?task_struct中的一個(gè)變量也是一個(gè)地址?,F(xiàn)在就相當(dāng)于我們知道了結(jié)構(gòu)體中一個(gè)變量的地址如何知道其他成員的地址呢。
結(jié)構(gòu)體偏移量:補(bǔ)一下結(jié)構(gòu)體偏移
了解的伙伴可以直接跳過
結(jié)構(gòu)體內(nèi)存對(duì)齊
題型考察結(jié)構(gòu)體的大小,我們來看一下例子引入,請問如下這段代碼輸出分別為什么:
struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; int i; }; int main() { printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2)); return 0; }
明明是兩個(gè)一樣的結(jié)構(gòu)體,為什么卻不一樣大,要知道答案我們就要知道結(jié)構(gòu)體的大小是如何計(jì)算的,結(jié)構(gòu)體大小的計(jì)算并不是單單就靠結(jié)構(gòu)體內(nèi)部元素的類型大小來決定,讓我們來看一下:
偏移量補(bǔ)充
?offsetof()? ? 這個(gè)宏可以計(jì)算結(jié)構(gòu)體某一個(gè)成員相較于起始位置的偏移量
頭文件:stddef.h
需要在宏中傳入的是:結(jié)構(gòu)體類型和結(jié)構(gòu)體變量名,下面我們來計(jì)算一下結(jié)構(gòu)體s1中成員的偏移量和S2結(jié)構(gòu)體的偏移量
struct S1 { char c1; int i; char c2; }; struct S2 { char c1; char c2; int i; }; int main() { /*printf("%d\n", sizeof(struct S1)); printf("%d\n", sizeof(struct S2));*/ printf("%d\n", offsetof(struct S1, c1)); printf("%d\n", offsetof(struct S1, i)); printf("%d\n", offsetof(struct S1, c2)); return 0; }
對(duì)齊規(guī)則?
1. 第一個(gè)成員在與結(jié)構(gòu)體變量偏移量為0的地址處。
2. 其他成員變量要對(duì)齊到某個(gè)數(shù)字(對(duì)齊數(shù))的整數(shù)倍的地址處。
對(duì)齊數(shù) = 編譯器默認(rèn)的一個(gè)對(duì)齊數(shù) 與 該成員大小的較小值。 VS中默認(rèn)的值為8 Linux中沒有默認(rèn)對(duì)齊數(shù),對(duì)齊數(shù)就是成員自身的大小
3. 結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(每個(gè)成員變量都有一個(gè)對(duì)齊數(shù))的整數(shù)倍。所有成員對(duì)齊數(shù)的最大值。
4. 如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對(duì)齊到自己的最大對(duì)齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整 體大小就是所有最大對(duì)齊數(shù)(含嵌套結(jié)構(gòu)體的對(duì)齊數(shù))的整數(shù)倍。
我們來看一下圖解S1的對(duì)齊:
?
對(duì)對(duì)齊規(guī)則第四條的解釋:
讓我們來看一下結(jié)構(gòu)體嵌套的對(duì)齊算法:
?如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對(duì)齊到自己的最大對(duì)齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整 體大小就是所有最大對(duì)齊數(shù)(含嵌套結(jié)構(gòu)體的對(duì)齊數(shù))的整數(shù)倍。
看一下這段代碼輸出風(fēng)別為多少
struct S3 { double d; char c; int i; }; struct S4 { char c1; struct S3 s3; double d; }; int main() { printf("%d\n", sizeof(struct S3)); printf("%d\n", sizeof(struct S4)); return 0; }
?
如果出現(xiàn)數(shù)組,就當(dāng)做多個(gè)同類型數(shù)據(jù)處理
為什么會(huì)有對(duì)齊
大部分的參考資料都是如是說的:
1. 平臺(tái)原因(移植原因): 不是所有的硬件平臺(tái)都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。比如某些平臺(tái)規(guī)定整型必須存放在4的整數(shù)倍地址處。
2. 性能原因: 數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊。 原因在于,為了訪問未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對(duì)齊的內(nèi)存訪問僅需要一次訪 問。
雖然浪費(fèi)了一些空間,但是換來了訪問效率的提升
總體來說: 結(jié)構(gòu)體的內(nèi)存對(duì)齊是拿空間來換取時(shí)間的做法。 那在設(shè)計(jì)結(jié)構(gòu)體的時(shí)候,我們既要滿足對(duì)齊,又要節(jié)省空間,如何做到: 讓占用空間小的成員盡量集中在一起。????????
?了解了結(jié)構(gòu)體偏移量,就可以利用偏移量,計(jì)算我們的task_struct的地址了:
&c = &首元素+偏移量。結(jié)構(gòu)體中成員越靠后地址越大
一個(gè)整型四個(gè)字節(jié),每個(gè)字節(jié)一個(gè)地址,拿到的是最小的,因?yàn)橛蓄愋?,也就是首地?偏移量拿到4個(gè)字節(jié)的內(nèi)容。數(shù)組訪問,結(jié)構(gòu)體成員訪問底層都是這個(gè)樣子。所以這里也是一樣,我們可以使用偏移量來訪問task_struct中所有的的其他屬性內(nèi)容。
&task_struct = &struct listnode+偏移量,那么這個(gè)偏移零的算法為:設(shè)我們一個(gè)節(jié)點(diǎn)為n
&((task_struct*)0->n);
&n-&((task_struct*)0->n) = 開始
(task_struct)(&n-&((task_struct*)0->n)) 得到task_struct地址
這既是linux內(nèi)核的實(shí)現(xiàn),會(huì)是一個(gè)宏的形式,這里是介紹會(huì)忽略掉一些類型轉(zhuǎn)換。
那么我們說再全局層面上,所有的進(jìn)程的pcb都要被鏈接入一個(gè)鏈表,方便我們的操作系統(tǒng)進(jìn)行管理,那么,當(dāng)我們的經(jīng)進(jìn)程需要排隊(duì)運(yùn)行的時(shí)候,需不要單獨(dú)將進(jìn)程從整體的雙鏈表上取下來單獨(dú)放入排隊(duì)等待這個(gè)隊(duì)列里面呢,答案是不用,
原因:誰規(guī)定我們的pcb里面只能有一個(gè)listnode這種結(jié)構(gòu)體呢,沒有人規(guī)定,那么我們的pcb中就可以有多個(gè)這種結(jié)構(gòu)體,一個(gè)用于將這個(gè)task_struct鏈接入整體的雙鏈表中進(jìn)行操作系統(tǒng)級(jí)別的管理,其他剩余的可以在不把我們的單個(gè)task_struct拿下來的前提下再鏈接入其他的數(shù)據(jù)結(jié)構(gòu),比如進(jìn)程排隊(duì)的隊(duì)列等等等等。
這個(gè)就叫一個(gè)task——struct可以被 鏈接進(jìn)入多種數(shù)據(jù)結(jié)構(gòu)的原因。
那么當(dāng)我們的操作系統(tǒng)刪除一個(gè)進(jìn)程的時(shí)候,一定會(huì)伴生著將當(dāng)前的進(jìn)程從鏈表、隊(duì)列中移除,但是所有的移除和task_struct沒有關(guān)系,只用移除struct listnode.
這就是進(jìn)程排隊(duì),那么進(jìn)程排隊(duì)是去哪里排隊(duì)呢?接下來看進(jìn)程狀態(tài)
2.操作系統(tǒng)學(xué)科層面對(duì)進(jìn)程狀態(tài)的理解
首先教材刪上的表述:
對(duì)于初學(xué)真的不好理解
2.1進(jìn)程的狀態(tài)理解
①我們說所謂的狀態(tài)就是一個(gè)整型變量,是task_struct中的一個(gè)整型變量
#define NEW 1
#define READY 2
#define RUNING 3
#define BLOCK 4 1
我們進(jìn)程的task_struct中
task_struct
{
int status;
};
也就是說我們未來進(jìn)程的狀態(tài)就取決于這個(gè)ststus變量里面保持的是數(shù)字幾,。
②.狀態(tài)決定了接下來的動(dòng)作
今天狀態(tài)不好今天想擺爛,今天狀態(tài)好,庫弛庫弛的寫幾套卷子,所以狀態(tài)決定了對(duì)象的后續(xù)動(dòng)作。linux中存在很多的進(jìn)程,每一個(gè)進(jìn)程都有一個(gè)狀態(tài),那么就可能存在多個(gè)進(jìn)程都要根據(jù)他的狀態(tài)執(zhí)行后續(xù)的動(dòng)作,而且還必然可能存在多個(gè)進(jìn)程的狀態(tài)是一樣的,(雖然進(jìn)程很多,狀態(tài)類型也多),所以就需要進(jìn)程排隊(duì)
2.2運(yùn)行狀態(tài)
一個(gè)cpu一個(gè)運(yùn)行隊(duì)列,(如果電腦中有兩個(gè)cpu,會(huì)有兩個(gè)運(yùn)行隊(duì)列,操作系統(tǒng)在內(nèi)核編譯的時(shí)候,識(shí)別到兩個(gè)cpu,就會(huì)創(chuàng)建兩個(gè)隊(duì)列,當(dāng)運(yùn)行多個(gè)進(jìn)程的時(shí)候,原則上,操作系統(tǒng)是要均衡的將進(jìn)程負(fù)載到兩個(gè)cpu上,這也成為SMP,負(fù)載均衡式的給兩個(gè)cpu平均分配資源,但是值得注意的是,一個(gè)進(jìn)程這次調(diào)度在這個(gè)cpu上,下一次還是被這個(gè)cpu調(diào)度。)當(dāng)我們的某個(gè)或者某些進(jìn)程的資源的準(zhǔn)備OK了,那么就可以進(jìn)進(jìn)程放入cpu的運(yùn)行隊(duì)列了,將進(jìn)程的task——struct放入運(yùn)行隊(duì)列:
(關(guān)于怎么排,在優(yōu)先級(jí)內(nèi)容里面講解)
?只要放在cpu上對(duì)應(yīng)的運(yùn)行隊(duì)列中1,我們就把該進(jìn)程當(dāng)前狀態(tài)稱為運(yùn)行狀態(tài)
(至于運(yùn)行隊(duì)列里面保存的是pcb結(jié)構(gòu)體還是只是節(jié)點(diǎn)struct listnode ,這和具體的系統(tǒng)和設(shè)備有關(guān),有些操作系統(tǒng)可能用的指針或者一樣的node,但是我們只要能夠理解要的是鏈接結(jié)構(gòu)就OK了。)
解釋:
一個(gè)進(jìn)程是不是只有在cpu上跑的時(shí)候才是運(yùn)行狀態(tài),對(duì)嗎?這樣的操作系統(tǒng)有,不過是在教材的描述里面,這樣的描述是在教材里面的描述,當(dāng)然,在cpu上運(yùn)行當(dāng)然是運(yùn)行狀態(tài),不過主流的操作系統(tǒng),只要在運(yùn)行隊(duì)列里面都可以叫做運(yùn)行狀態(tài)。所以,所謂的創(chuàng)建和就緒在實(shí)際的操作系統(tǒng)中并沒有實(shí)現(xiàn),要運(yùn)行就放在運(yùn)行隊(duì)列,不運(yùn)行就放在常量隊(duì)列里面。所以r狀態(tài)就表明進(jìn)程已經(jīng)準(zhǔn)備好隨時(shí)被調(diào)度了,所以就緒和執(zhí)行被二合一,因?yàn)檫@兩個(gè)獨(dú)立的狀態(tài)看不到。
2.3 阻塞狀態(tài)?
以硬件為例子:
os對(duì)硬件進(jìn)行管理
先對(duì)硬件的屬性進(jìn)行描述
struct device
{
int tyoe;//1表示鍵盤,2,表示網(wǎng)卡,3表示磁盤......
//設(shè)備的操作方法,
//設(shè)備的狀態(tài)
struct listnode n;
:::
:::
:::
};
在將各個(gè)設(shè)備使用數(shù)據(jù)結(jié)組織起來
和進(jìn)程的管理方式一模一樣,這就是操作系統(tǒng)為了對(duì)硬件做出管理做出的數(shù)據(jù)結(jié)構(gòu)描述對(duì)象,
當(dāng)前假設(shè)我們的計(jì)算機(jī)內(nèi)部是這個(gè)樣子:
現(xiàn)在比如說我們運(yùn)行這個(gè)程序:
當(dāng)我們程序運(yùn)行起來,是不是就意味著此時(shí)進(jìn)程需要等待鍵盤輸入,不能繼續(xù)往下面執(zhí)行了,除非鍵盤輸入?。所以,此時(shí)操作系統(tǒng)就來了,這個(gè)進(jìn)程在等待什資源,就將這個(gè)進(jìn)程的狀態(tài)從運(yùn)行狀態(tài)改為非運(yùn)行狀態(tài),我們可以暫時(shí)稱為阻塞狀態(tài),然后將這個(gè)進(jìn)程放到我們對(duì)應(yīng)設(shè)備資源的隊(duì)列當(dāng)中。我們的cpu也是屬于設(shè)備的,只不過是一個(gè)跑得快的設(shè)備,那么cpu可以有進(jìn)程的運(yùn)行列表隊(duì)列,我們其他的設(shè)備一樣的可以有我們的進(jìn)程隊(duì)列,當(dāng)我們的進(jìn)程需要對(duì)應(yīng)的設(shè)備資源的時(shí)候,這個(gè)時(shí)候要等待這個(gè)設(shè)備就緒(鍵盤輸入)才能繼續(xù)執(zhí)行,進(jìn)程不能繼續(xù)執(zhí)行就從運(yùn)行隊(duì)列連入到我們的設(shè)備進(jìn)程隊(duì)列里面,因?yàn)榭赡苄枰@個(gè)設(shè)備資源的進(jìn)程不止這一個(gè)。
接下來,cpu就不會(huì)調(diào)度這些連入底層設(shè)備調(diào)度隊(duì)列里面的進(jìn)程了,cpu只會(huì)調(diào)度在運(yùn)行隊(duì)列里面的進(jìn)程,所以這個(gè)進(jìn)程后面代碼不執(zhí)行了,也就是程序卡在這個(gè)地方等待輸入。當(dāng)我們在鍵盤輸入數(shù)據(jù)了,而我們的鍵盤輸入沒有數(shù)據(jù),也就是硬件的就緒狀態(tài),只有我們的操作系統(tǒng)清楚,操作系統(tǒng)可是我們硬件的管理者,所有用戶按鍵過后,鍵盤工作OK了,操作系統(tǒng)就將這個(gè)進(jìn)程將這個(gè)進(jìn)程移到cpu的運(yùn)行隊(duì)列里面,將阻塞狀態(tài)改為運(yùn)行狀態(tài),我們操作系統(tǒng)的工作就完成了,剩下的就交給cpu去調(diào)度。(以上對(duì)兩個(gè)進(jìn)程pcb描述的移動(dòng),只是形象化的說法,我們進(jìn)程的pcb是不會(huì)也不能刪除的,只不過的是改變pcb里面對(duì)應(yīng)的鏈接字段也就是listnode)
我們不要用人類的感受衡量計(jì)算機(jī)的速度,此時(shí)再次執(zhí)行到輸入的話,我們的設(shè)備狀態(tài)就緒(理解為操作系統(tǒng)已經(jīng)將數(shù)據(jù)從外設(shè)搬到內(nèi)存了),有數(shù)據(jù)了,就可以繼續(xù)往下執(zhí)行。
結(jié)論:
當(dāng)我們的進(jìn)程在進(jìn)行等待軟硬件資源的,資源如果沒有就緒,我們的進(jìn)程task_struct只能將自己設(shè)置為阻塞狀態(tài),將自己的pcb連入等待的資源提供的等待隊(duì)列中。
所以狀態(tài)的變遷,引起的是pcb搬遷到不同的隊(duì)列中。
2.4 掛起狀態(tài)
掛起狀態(tài)在linux中并不是一個(gè)比較常見的狀態(tài),大部分情況下計(jì)算機(jī)極少出現(xiàn)這種情況
掛起狀態(tài)的前提:
計(jì)算機(jī)資源比較吃緊,也就是說計(jì)算機(jī)的內(nèi)存資源比較吃緊了,(并不排除其他情況)
進(jìn)程在阻塞狀態(tài)的時(shí)候,我們的程序的代碼和數(shù)據(jù)是不會(huì)被執(zhí)行和訪問的,那么這個(gè)時(shí)候,這個(gè)進(jìn)程又不會(huì)被調(diào)度,但是進(jìn)程的代碼和數(shù)據(jù)又占據(jù)著內(nèi)存的空間,如果此時(shí)計(jì)算機(jī)內(nèi)存已經(jīng)很吃緊了,操作系統(tǒng)很大可能就會(huì)將這個(gè)進(jìn)程的代碼和數(shù)據(jù)放回磁盤中,當(dāng)這個(gè)進(jìn)程快要被調(diào)度的時(shí)候,再拿回內(nèi)存。這個(gè)我們稱之為:阻塞掛起。還有運(yùn)行掛起等等
(進(jìn)程會(huì)等待某種資源,此時(shí)進(jìn)程大概率是不會(huì)被執(zhí)行的,這種資源如果短期內(nèi)不會(huì)就行,進(jìn)程就在阻塞隊(duì)列等待,如果突然計(jì)算機(jī)內(nèi)存資源非常吃緊,cpu和整機(jī)資源越來越少,甚至嚴(yán)重不足,已經(jīng)無法為上層運(yùn)行分配內(nèi)存,此時(shí)對(duì)于計(jì)算機(jī)來說即將面臨大量操作的失敗,操作向上提供好的環(huán)境其他就無了,操作系統(tǒng)也面臨著崩潰問題,要么擺爛崩潰,要么搞點(diǎn)內(nèi)存,所以操作系統(tǒng)就會(huì)對(duì)整個(gè)機(jī)器的每個(gè)內(nèi)存做說明,有些內(nèi)存現(xiàn)在就要用,有些現(xiàn)在不用卻占著內(nèi)存比如阻塞狀態(tài)進(jìn)程,那么就造成資源的浪費(fèi),所以操作系統(tǒng)就嘗試將這個(gè)進(jìn)程的代碼和數(shù)據(jù)交換到磁盤中,磁盤有固定的區(qū)域,是為了在內(nèi)存緊張的時(shí)候用于和操作系統(tǒng)進(jìn)行數(shù)據(jù)的換入換出叫做swap分區(qū),
真正當(dāng)內(nèi)存吃緊,操作系統(tǒng)會(huì)將很多很多進(jìn)程的代碼和數(shù)據(jù)換入換出,節(jié)省空間,還有一些為網(wǎng)絡(luò)預(yù)留的空間也會(huì)被釋放,也就是說整個(gè)系統(tǒng)中能被釋放的都要釋放。傳出到磁盤叫換出,等要用或者即將用到的時(shí)候再加載回來叫換入,一但對(duì)應(yīng)的進(jìn)程對(duì)應(yīng)的數(shù)據(jù)和代碼不在內(nèi)存中,就稱當(dāng)前狀態(tài)為掛起狀態(tài)。特指阻塞掛起)
所以所謂掛起,就是將進(jìn)程的代碼和數(shù)據(jù)寫入到外部的磁盤,當(dāng)進(jìn)程要被調(diào)度的時(shí)候再放回來,這是臨時(shí)的騰空間,這是操作系統(tǒng)在內(nèi)存不足的情況下進(jìn)行輾轉(zhuǎn)騰挪,想把空間變大一點(diǎn),要是內(nèi)存分配不足,操作系統(tǒng)掛了,整個(gè)就掛了,慢點(diǎn)總比掛了好。
思考:,換入換出的時(shí)候,pcb數(shù)據(jù)結(jié)構(gòu)是不會(huì)被換出的,因?yàn)椴僮飨到y(tǒng)就是根據(jù)屬性知道的進(jìn)程處于什么狀態(tài)。
創(chuàng)建進(jìn)程是先創(chuàng)建pcb還是先將進(jìn)程的代碼和數(shù)據(jù)先加載到內(nèi)存?
如果我們的內(nèi)存很小,比如只有16G,但是我們現(xiàn)在要運(yùn)行80g的游戲,
只會(huì)先加載一部分, 自己的代碼和數(shù)據(jù)剛開始可能非常大,根本加載不完,只會(huì)先加載一批,那么當(dāng)前進(jìn)程該不該運(yùn)行呢?先將pcb保存內(nèi)存里,沒有代碼和數(shù)據(jù)也不影響,未來也可以換入,再調(diào)度執(zhí)行,所以雙擊運(yùn)行的時(shí)候,會(huì)先創(chuàng)建管理字段,創(chuàng)建完,內(nèi)存和字段都還不會(huì)加載到內(nèi)存。
輔助理解例子:
我們高考畢業(yè)的時(shí)候,人還沒有到學(xué)校,但是學(xué)校在錄取我們的時(shí)候就已經(jīng)將我們的檔案拿走了,等我們九月份開學(xué)的時(shí)候,我們該在那個(gè)班級(jí),學(xué)號(hào)是多少,都被排得明明白白,就好比,進(jìn)程運(yùn)行的時(shí)候,先將pcb建立出來,再將數(shù)據(jù)等加載,然后就靜等調(diào)度了。
既然數(shù)據(jù)要換入換出,可以將磁盤分區(qū)搞大些嗎?絕對(duì)可以,操作系統(tǒng)hold得住,但是一般在系統(tǒng)設(shè)置的時(shí)候,內(nèi)存分區(qū)的大小一般為內(nèi)存的一半或者就是內(nèi)存的大小,最多不超過內(nèi)存的兩倍。
理由:整體的換入換出是將數(shù)據(jù)進(jìn)行拷貝的過程,馮諾依曼體系告訴我們,計(jì)算機(jī)當(dāng)中,本質(zhì)進(jìn)行數(shù)據(jù)訪問訪問外設(shè)速度是比較慢的,在阻塞掛起時(shí),換入換出,使用效率換系統(tǒng)可用性,就是運(yùn)行變慢和系統(tǒng)可用之間選擇了系統(tǒng)可用。如果分區(qū)設(shè)置非常大,系統(tǒng)就會(huì)很依賴這個(gè)swap分區(qū),想辦法將這個(gè)用完,那么操作系統(tǒng)和外設(shè)的io操作比重就很高,效率就會(huì)越來越慢。
3.linux下具體的進(jìn)程狀態(tài)(重要)
具體看看具體操作系統(tǒng)下面什么是運(yùn)行、什么是阻塞、掛起(代碼中不是很好看就不展示)
linux內(nèi)核代碼對(duì)于進(jìn)程的描述
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
- R運(yùn)行狀態(tài)(running): 并不意味著進(jìn)程一定在運(yùn)行中,它表明進(jìn)程要么是在運(yùn)行中要么在運(yùn)行隊(duì)列 里。
- S睡眠狀態(tài)(sleeping): 意味著進(jìn)程在等待事件完成(這里的睡眠有時(shí)候也叫做可中斷睡眠?
- D磁盤休眠狀態(tài)(Disk sleep)有時(shí)候也叫不可中斷睡眠狀態(tài)(uninterruptible sleep),在這個(gè)狀態(tài)的 進(jìn)程通常會(huì)等待IO的結(jié)束。
- T停止?fàn)顟B(tài)(stopped): 可以通過發(fā)送 SIGSTOP 信號(hào)給進(jìn)程來停止(T)進(jìn)程。這個(gè)被暫停的進(jìn)程可 以通過發(fā)送 SIGCONT 信號(hào)讓進(jìn)程繼續(xù)運(yùn)行。
- X死亡狀態(tài)(dead):這個(gè)狀態(tài)只是一個(gè)返回狀態(tài),你不會(huì)在任務(wù)列表里看到這個(gè)狀態(tài)。
- Z(zombie)-僵尸進(jìn)程 僵死狀態(tài)(Zombies)是一個(gè)比較特殊的狀態(tài)。當(dāng)進(jìn)程退出并且父進(jìn)程(使用wait()系統(tǒng)調(diào)用,后面講) 沒有讀取到子進(jìn)程退出的返回代碼時(shí)就會(huì)產(chǎn)生僵死(尸)進(jìn)程 僵死進(jìn)程會(huì)以終止?fàn)顟B(tài)保持在進(jìn)程表中,并且會(huì)一直在等待父進(jìn)程讀取退出狀態(tài)代碼。 所以,只要子進(jìn)程退出,父進(jìn)程還在運(yùn)行,但父進(jìn)程沒有讀取子進(jìn)程狀態(tài),子進(jìn)程進(jìn)入Z狀態(tài)?
3.1 R和s?
編寫一段代碼如下,執(zhí)行:
查看我們的進(jìn)程:
stat就描述了當(dāng)前進(jìn)程的狀態(tài)?
為什么程序在運(yùn)行,狀態(tài)卻是S,睡眠狀態(tài)
因?yàn)閏pu執(zhí)行我們這行輸出的時(shí)間非??欤缓笪覀儗懥藄leep,睡眠1秒,那么對(duì)于cpu來說,整個(gè)程序很長的時(shí)間都是在sleep,那么這個(gè)狀態(tài)顯示就不奇怪了
接下來修改一下代碼,注釋掉sleep
代碼瘋狂運(yùn)行:
狀態(tài)還是s?
因?yàn)椋覀冏约旱拇a中有printf,向顯示器打印,這個(gè)程序在遠(yuǎn)端服務(wù)器跑的,當(dāng)進(jìn)行執(zhí)行的時(shí)候,結(jié)果打印在我們的電腦上,printf是要訪問外設(shè)的,不論是網(wǎng)絡(luò)設(shè)備還是顯示屏,都是要訪問外設(shè)的,那么我們想打印的時(shí)候,我們的設(shè)備就緒了嗎?,cpu可以比其他外設(shè)的速度快多了,第一次打印了,第二次能保證數(shù)據(jù)已經(jīng)刷新到設(shè)備了嗎?當(dāng)執(zhí)行打印代碼,將數(shù)據(jù)執(zhí)行到緩沖區(qū),設(shè)備來得及刷新嗎?不一定,所以printf里面就伴生著使得設(shè)備處于就緒狀態(tài)的東西,顯示是睡眠狀態(tài),就說明當(dāng)期進(jìn)程百分之95的時(shí)間都是在等待,只有百分之五的時(shí)間在運(yùn)行。
我們再修改代碼:
?
此時(shí)就是運(yùn)行狀態(tài)了(不訪問任何外設(shè)還是個(gè)死循環(huán))
在目前系統(tǒng)里,可能有多個(gè)進(jìn)程,只有在運(yùn)行隊(duì)列里才是運(yùn)行狀態(tài),大部分進(jìn)程等待外設(shè)是非運(yùn)行狀態(tài)
(top)命令
為什么grep總是r
grep過濾也是一個(gè)進(jìn)程,只有被調(diào)度時(shí)才可以幫我們打印過濾結(jié)果,把自己過濾出來,就相當(dāng)于自己知道自己是什么狀態(tài),所以一睜眼就知道自己醒著。
?+:前后臺(tái)程序
表示這個(gè)程序是前臺(tái)進(jìn)程還是后臺(tái)進(jìn)程(信號(hào)文章詳細(xì)講解)
一般程序在運(yùn)行的時(shí)候,我們將程序稱為前臺(tái)程序
此時(shí)的狀態(tài)為:s+
?我們執(zhí)行一些指令時(shí)沒有辦法截停這個(gè)程序,當(dāng)我們執(zhí)行ctrc就可以截停
此時(shí)我們在執(zhí)行程序命令后面+ &
?我們運(yùn)行一些指令也是可以的:
發(fā)現(xiàn)按住ctrls 無法截停
?當(dāng)前進(jìn)程狀態(tài)為:
就為s沒有+號(hào),這種進(jìn)程我們就稱為后臺(tái)進(jìn)程,前臺(tái)進(jìn)程可以被鍵盤直接殺掉,后臺(tái)進(jìn)程不可以,只能通過kill -9 進(jìn)程pid 來殺死
批量化下載等可以在指令后面+&
3.2 休眠狀態(tài)s
編寫代碼如下:
?
在等待鍵盤輸入,linux中的休眠就等價(jià)于操作系統(tǒng)學(xué)科中的阻塞狀態(tài)。
阻塞描述的是宏觀,在linux中是s狀態(tài),但是不僅僅只有上述一種阻塞s狀態(tài),?
這種進(jìn)程可以通過ctrlc結(jié)束,所以我們也將linux中的這種狀態(tài)稱為可中斷睡眠,也稱為淺度睡眠
3.3 disk sleep? ? ?不可中斷睡眠也叫作深度睡眠。
舉例來說當(dāng)我們的進(jìn)程要將一塊很大的數(shù)據(jù)給我們的磁盤,磁盤比較慢,進(jìn)程就一直等待等待,那么在內(nèi)存比較吃緊的時(shí)候,操作系統(tǒng)也是會(huì)移除某些進(jìn)程的,操作系統(tǒng)過來一看你什么也不干,還在這里占著內(nèi)存,將移除了這個(gè)進(jìn)程,然后此時(shí)如果我們磁盤出了異常,拷貝的數(shù)據(jù)找不到來頭,無法繼續(xù)拷貝就丟棄了,這不行,當(dāng)這個(gè)進(jìn)程一直等待,操作系統(tǒng)也不能將他移除,就叫深度睡眠。
3.4 T狀態(tài):進(jìn)程變成暫停狀態(tài)
某個(gè)進(jìn)程要訪問某種資源,但是呢某種資源又不能被這個(gè)進(jìn)程所訪問,操作系統(tǒng)又不想殺掉這個(gè)進(jìn)程,但是為了不讓這個(gè)進(jìn)程做某些非法的操作,就將進(jìn)程設(shè)置為暫停狀態(tài)。
kill -l 命令·可以·查看信號(hào)
9號(hào)命令可以幫我們殺死一個(gè)進(jìn)程
19號(hào)命令:給一個(gè)進(jìn)程發(fā)送一個(gè)暫停信號(hào)
狀態(tài)變?yōu)闀和#瑳]有+號(hào)?,進(jìn)程一但暫停,就從前臺(tái)轉(zhuǎn)變?yōu)楹笈_(tái)進(jìn)程了。
18 號(hào)信號(hào),進(jìn)程繼續(xù)啟動(dòng):
?
3.5 t狀態(tài)
?我們調(diào)試這段代碼:
我們在輸入出打上斷點(diǎn)過后,讓程序運(yùn)行后我們看一下進(jìn)程的狀態(tài)
進(jìn)程會(huì)變成t狀態(tài),也是一種暫停,是一種被追蹤的狀態(tài)?
等待我們輸入或者按下n
?等待也是一種阻塞狀態(tài)。
3.6 z狀態(tài):僵尸狀態(tài)
進(jìn)程創(chuàng)建的目的是讓進(jìn)程幫助完成某種任務(wù),任務(wù)死掉了,我們要獲取進(jìn)程的退出數(shù)據(jù),是正常退出還是遇到異常,退出后,代碼和數(shù)據(jù)可以直接釋放,因?yàn)檫M(jìn)程死掉了,后續(xù)的代碼不會(huì)再執(zhí)行了,但是進(jìn)程pcb不應(yīng)該立馬釋放,應(yīng)該緩一下,為了未來讓系統(tǒng)或者其他進(jìn)程獲取一下他的退出數(shù)據(jù),只有進(jìn)程的退出數(shù)據(jù)被拿走之后,這個(gè)進(jìn)程才是正常死亡狀態(tài),pcb才可以被釋放。
我們將一個(gè)進(jìn)程執(zhí)行完畢,但是當(dāng)前并沒有去獲取這個(gè)進(jìn)程的退出數(shù)據(jù)時(shí),我們將這個(gè)進(jìn)程的狀態(tài)稱為僵尸狀態(tài)。
通常情況下,是由父進(jìn)程產(chǎn)生子進(jìn)程,當(dāng)子進(jìn)程退出,父進(jìn)程就必須去讀取一下子進(jìn)程的退出信息,怎么讀取,不是目前所關(guān)心的話題,如果父進(jìn)程不讀取,這個(gè)子進(jìn)程的代碼和數(shù)據(jù)被釋放,但是pcb不能被釋放,其中包含了退出時(shí)進(jìn)程的狀態(tài)信息。
程序前五秒父進(jìn)程和子進(jìn)程一起運(yùn)行,五秒過后,子進(jìn)程退出, 就應(yīng)該看見子進(jìn)程變成僵尸
defunt就是無用的,失效的,死亡的意思?
進(jìn)程退出,pcb不能釋放,保存退出時(shí)的狀態(tài)信息,等待父進(jìn)程讀取,進(jìn)程的代碼和數(shù)據(jù)合約先釋放:
接下來我們讓父進(jìn)程讀一下,然后看一下僵尸進(jìn)程的消失:
前五秒父子進(jìn)程同時(shí)運(yùn)行,然后中間五秒子進(jìn)程變成僵尸,十秒后,父進(jìn)程回收子進(jìn)程
為什么維持僵尸狀態(tài):
未來我們創(chuàng)建進(jìn)程是希望這個(gè)進(jìn)程給用戶完成某種工作的,肯定要求一個(gè)結(jié)果,將子進(jìn)程創(chuàng)建出來,那么子進(jìn)程必須要有結(jié)果數(shù)據(jù),這個(gè)數(shù)據(jù)是保存在pcb中,子進(jìn)程結(jié)束,代碼和數(shù)據(jù)可以釋放,但是pcb還不能被釋放
什么是z狀態(tài):
進(jìn)程已經(jīng)退出,但是當(dāng)前進(jìn)程的狀態(tài)需要自己維持住,供上層讀取,那么就必須處于z狀態(tài)
只有當(dāng)通過我們的方式讀取pcb里面的退出信息了,才能變成x死亡狀態(tài)
如果不讀取,僵尸狀態(tài)會(huì)一直存在,是會(huì)有影響的,一直存在疑問著task_struct會(huì)一直存在,維護(hù)這個(gè)對(duì)象都是要占據(jù)內(nèi)存的。這塊內(nèi)存得不到釋放又不能使用叫做內(nèi)存泄漏。所以父進(jìn)程必須等待,所有的進(jìn)程都必須先到z,再到x。bash父進(jìn)程會(huì)自動(dòng)讀取,其余父進(jìn)程不確定,可能自己讀取,可能像我們上面一樣要手動(dòng)讀取等待。bash命令行會(huì)主動(dòng)回收子進(jìn)程狀態(tài),所以看不到。
那么這張圖在我們linux下是適用的
4. 孤兒進(jìn)程
修改代碼如下:
?
父進(jìn)程先退出,父進(jìn)程很難查到自己的僵尸狀態(tài),bash會(huì)回收,那么父進(jìn)程先退出了,為了子進(jìn)程變僵尸,誰來回收呢??
父進(jìn)程先掛掉,子進(jìn)程會(huì)被1號(hào)進(jìn)程領(lǐng)養(yǎng),我們可以理解為操作系統(tǒng),或者說父進(jìn)程先結(jié)束,子進(jìn)程必須被操作系統(tǒng)領(lǐng)養(yǎng),因?yàn)闊o主進(jìn)程,一直僵尸浪費(fèi)資源,我們將這種進(jìn)程稱之為孤兒進(jìn)程。 還會(huì)將自己變成后臺(tái)進(jìn)程。
孤兒進(jìn)程也是可以有子進(jìn)程的。?文章來源:http://www.zghlxwxcb.cn/news/detail-851153.html
5.結(jié)語
今天的主要內(nèi)容是進(jìn)程的排隊(duì),進(jìn)程狀態(tài)在操作系統(tǒng)學(xué)科層次的理解和linux操作系統(tǒng)下的觀察。擴(kuò)展了很多內(nèi)容,補(bǔ)充了僵尸進(jìn)程和孤兒進(jìn)程,后續(xù)進(jìn)程的優(yōu)先級(jí)和進(jìn)程相關(guān)內(nèi)容,歡迎大家關(guān)注。創(chuàng)作不易,如果大家覺得有所收獲,歡迎關(guān)注,一起交流互進(jìn)。?文章來源地址http://www.zghlxwxcb.cn/news/detail-851153.html
到了這里,關(guān)于【Linux】進(jìn)程的狀態(tài)(運(yùn)行、阻塞、掛起)詳解,揭開孤兒進(jìn)程和僵尸進(jìn)程的面紗,一篇文章萬字講透?。。?!進(jìn)程的學(xué)習(xí)②的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!