一、進(jìn)程排隊(duì)的理解
????????進(jìn)程不是一直運(yùn)行的,進(jìn)程可能會(huì)在等待某種軟硬件資源。即使把進(jìn)程加載到CPU中,也不是一直會(huì)運(yùn)行的。而進(jìn)程排隊(duì),一定是在等待某種軟硬件資源(可以是CPU,鍵盤(pán),磁盤(pán),網(wǎng)卡等等設(shè)備......),排隊(duì)時(shí)是進(jìn)程的PCB在排隊(duì)。在這里就需要引入一個(gè)概念:一個(gè)PCB可以被鏈入多種數(shù)據(jù)結(jié)構(gòu)中。在之前的博客中也說(shuō)過(guò),PCB其實(shí)就是描述進(jìn)程的一個(gè)很大的結(jié)構(gòu)體,在這個(gè)結(jié)構(gòu)體中,包含有很多其他的結(jié)構(gòu)體。比如我定義一個(gè)node結(jié)構(gòu)體
struct node
{
struct node* prev;
struct node* next;
}
????????該結(jié)構(gòu)體可以指向它的前一個(gè)節(jié)點(diǎn),也可以指向它的后一個(gè)節(jié)點(diǎn)。也就是說(shuō),進(jìn)程排隊(duì)不是我們簡(jiǎn)單地理解的是進(jìn)程的PCB去排隊(duì),而是PCB內(nèi)部的各個(gè)結(jié)構(gòu)體通過(guò)prev指針和next指針連接起各個(gè)進(jìn)程去排隊(duì),從而可以讓進(jìn)程在不同的隊(duì)列中進(jìn)行排隊(duì)。如下圖所示。也就是說(shuō),當(dāng)我們要把某一個(gè)task_struct結(jié)構(gòu)體鏈入某一個(gè)隊(duì)列中時(shí),我們不必把它從全局雙鏈表中移除,可以直接鏈入隊(duì)列中。
????????通過(guò)task_struct結(jié)構(gòu)體內(nèi)部listnode相對(duì)于task_struct結(jié)構(gòu)體首地址的地址偏移量,也可以找到task_struct結(jié)構(gòu)體開(kāi)始的地址,進(jìn)而找到task_struct結(jié)構(gòu)體。
二、進(jìn)程狀態(tài)的表述--運(yùn)行、阻塞、掛起
運(yùn)行狀態(tài)
????????所謂的狀態(tài),本質(zhì)就是一個(gè)整形變量,是在task_struct中的一個(gè)整形變量。狀態(tài)決定了你的后續(xù)動(dòng)作。Linux中可能存在多個(gè)進(jìn)程都要根據(jù)它的狀態(tài)執(zhí)行后續(xù)動(dòng)作。一個(gè)CPU都會(huì)維護(hù)一個(gè)運(yùn)行隊(duì)列,當(dāng)一個(gè)進(jìn)程的PCB被鏈入到CPU的運(yùn)行隊(duì)列中時(shí),我們就稱這個(gè)進(jìn)程的狀態(tài)為運(yùn)行狀態(tài)。也就是說(shuō),并不是當(dāng)進(jìn)程在CPU上運(yùn)行的時(shí)候它才是運(yùn)行狀態(tài),只要進(jìn)程的PCB被鏈入到CPU的運(yùn)行隊(duì)列中,我們就可以成進(jìn)程處于運(yùn)行狀態(tài)了。運(yùn)行狀態(tài)表示進(jìn)程已經(jīng)隨時(shí)準(zhǔn)備好接受CPU的調(diào)度了。
阻塞狀態(tài)
????????在操作系統(tǒng)層面上,為了管理好底層的硬件,其實(shí)操作系統(tǒng)也是把硬件都描述成一個(gè)一個(gè)的結(jié)構(gòu)體,其中在硬件的結(jié)構(gòu)體中,就有像CPU的運(yùn)行隊(duì)列一樣的等待隊(duì)列,當(dāng)一個(gè)進(jìn)程比如執(zhí)行到scanf函數(shù)必須等待鍵盤(pán)資源時(shí),操作系統(tǒng)就會(huì)將該進(jìn)程的PCB從CPU的運(yùn)行隊(duì)列中移除,將表示進(jìn)程狀態(tài)的整形變量設(shè)置為block,再將該進(jìn)程的PCB鏈入到鍵盤(pán)結(jié)構(gòu)體的等待隊(duì)列中。該進(jìn)程就開(kāi)始等待鍵盤(pán)資源了,這個(gè)狀態(tài)我們就稱之為阻塞狀態(tài)。當(dāng)鍵盤(pán)讀到了用戶輸入的數(shù)據(jù),操作系統(tǒng)再將該進(jìn)程的PCB從鍵盤(pán)的等待隊(duì)列中移除,鏈入到CPU的運(yùn)行隊(duì)列中,再改變表示進(jìn)程狀態(tài)的整形變量,從而實(shí)現(xiàn)了進(jìn)程狀態(tài)的切換。上面的例子可以總結(jié)為,當(dāng)我們的進(jìn)程在等待軟硬件資源的時(shí)候,資源如果沒(méi)有就緒,我們的進(jìn)程PCB只能1、設(shè)置進(jìn)程狀態(tài)為阻塞狀態(tài),2、將自己的PCB鏈入等待資源的等待隊(duì)列中。進(jìn)一步的我們也可以了解到,進(jìn)程狀態(tài)的變遷,引起的是進(jìn)程的PCB會(huì)被操作系統(tǒng)鏈入到不同的隊(duì)列中。
掛起狀態(tài)
阻塞掛起
?前提:計(jì)算機(jī)資源已經(jīng)比較吃緊。當(dāng)一個(gè)進(jìn)程想要被CPU調(diào)度運(yùn)行時(shí),它對(duì)應(yīng)的代碼和數(shù)據(jù)勢(shì)必要加載到內(nèi)存當(dāng)中??墒侨绻@個(gè)進(jìn)程此時(shí)正處于阻塞狀態(tài)且對(duì)應(yīng)的硬件資源一時(shí)半會(huì)兒不會(huì)得到相應(yīng),而此時(shí)計(jì)算機(jī)的內(nèi)存資源又比較吃緊的情況下,操作系統(tǒng)就會(huì)將這個(gè)進(jìn)程對(duì)應(yīng)的代碼和數(shù)據(jù)先存放到磁盤(pán)對(duì)應(yīng)的swap分區(qū)中,讓其它比較重要的進(jìn)程優(yōu)先占有內(nèi)存,這個(gè)動(dòng)作就叫做喚出動(dòng)作。等到計(jì)算機(jī)資源相對(duì)寬裕,硬件資源響應(yīng)時(shí),再將這個(gè)進(jìn)程對(duì)應(yīng)的代碼和數(shù)據(jù)加載到內(nèi)存中,這個(gè)動(dòng)作就叫做喚入。一旦該進(jìn)程的PCB在內(nèi)存中創(chuàng)建出來(lái)了而對(duì)應(yīng)的代碼和數(shù)據(jù)不在內(nèi)存當(dāng)中,我們就稱這個(gè)進(jìn)程為掛起狀態(tài)。
三、Linux中具體的進(jìn)程狀態(tài)
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í)候也叫做可中斷睡眠/淺度睡眠?(interruptible sleep)),是阻塞狀態(tài)的一種。
D磁盤(pán)休眠狀態(tài)(Disk sleep)有時(shí)候也叫不可中斷睡眠狀態(tài)/深度睡眠(uninterruptible sleep):在這個(gè)狀態(tài)的進(jìn)程通常會(huì)等待IO的結(jié)束,處于D狀態(tài)的進(jìn)程在系統(tǒng)資源吃緊的時(shí)候也不會(huì)被操作系統(tǒng)殺死,也是阻塞狀態(tài)的一種。
T停止?fàn)顟B(tài)(stopped): 可以通過(guò)發(fā)送 SIGSTOP 信號(hào)給進(jìn)程來(lái)停止(T)進(jìn)程,也是阻塞狀態(tài)的一種。這個(gè)被暫停的進(jìn)程可以通過(guò)發(fā)送 SIGCONT 信號(hào)讓進(jìn)程繼續(xù)運(yùn)行,進(jìn)程暫停以后就變成了后臺(tái)進(jìn)程。kill -19 進(jìn)程標(biāo)識(shí)符。kill -18?進(jìn)程標(biāo)識(shí)符:讓這個(gè)進(jìn)程繼續(xù)運(yùn)行。
X死亡狀態(tài)(dead):這個(gè)狀態(tài)只是一個(gè)返回狀態(tài),你不會(huì)在任務(wù)列表里看到這個(gè)狀態(tài)
?????????上面這一段程序是一段死循環(huán),當(dāng)我把它運(yùn)行起來(lái)時(shí),我們可以看到:
?????????當(dāng)前我這個(gè)進(jìn)程是處于睡眠狀態(tài)(S狀態(tài))的。這是因?yàn)槲业倪@個(gè)程序中執(zhí)行了printf函數(shù),printf函數(shù)是要訪問(wèn)外設(shè)的,要訪問(wèn)外設(shè)就不可避免的要等待外設(shè)響應(yīng)。而CPU的運(yùn)行速度是非常非常快的,也就是說(shuō)相對(duì)CPU而言,該進(jìn)程大部分時(shí)間還是在等待外設(shè)的,在等待過(guò)程中CPU就將該進(jìn)程鏈入到外設(shè)的等待隊(duì)列中,所以該進(jìn)程查到的狀態(tài)大部分都是睡眠狀態(tài),這里的Linux操作系統(tǒng)具體實(shí)現(xiàn)的S狀態(tài)就是上面操作系統(tǒng)層面上的阻塞狀態(tài)的一種。S后面這個(gè)+號(hào)表示該進(jìn)程是前臺(tái)進(jìn)程,沒(méi)有+號(hào)表示該進(jìn)程是后臺(tái)進(jìn)程。
僵尸狀態(tài)(Z狀態(tài))
????????當(dāng)子進(jìn)程退出時(shí),父進(jìn)程就必須去讀取子進(jìn)程退出時(shí)的退出狀態(tài)。如果父進(jìn)程不讀取子進(jìn)程退出時(shí)的退出狀態(tài),子進(jìn)程的PCB就不會(huì)被系統(tǒng)釋放,子進(jìn)程就會(huì)一直處于僵尸狀態(tài)。創(chuàng)建子進(jìn)程是為了讓這個(gè)子進(jìn)程給用戶完成工作的,子進(jìn)程完成工作后必須得有結(jié)果數(shù)據(jù),這些數(shù)據(jù)都保存在子進(jìn)程的PCB中。這就是為什么要有僵尸狀態(tài)的原因,是為了獲得子進(jìn)程的結(jié)果數(shù)據(jù)。如果父進(jìn)程不讀取,那么這個(gè)僵尸狀態(tài)的進(jìn)程會(huì)一直存在,會(huì)引起內(nèi)存泄漏,造成系統(tǒng)資源的浪費(fèi)。
? ? ? ? 為什么我們?cè)谥暗倪M(jìn)程沒(méi)有見(jiàn)過(guò)處于Z狀態(tài)呢?那是因?yàn)橐郧?/strong>我們創(chuàng)建的進(jìn)程的父進(jìn)程都是bash,bash一瞬間會(huì)自動(dòng)讀取子進(jìn)程的退出狀態(tài),不需要我們手動(dòng)讀取。而我們自己創(chuàng)建的子進(jìn)程需要我們自己讀取它的退出狀態(tài)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-842030.html
?四、孤兒進(jìn)程
????????當(dāng)父進(jìn)程先于子進(jìn)程退出,子進(jìn)程會(huì)被操作系統(tǒng)(1號(hào)進(jìn)程)領(lǐng)養(yǎng),這個(gè)子進(jìn)程就叫做孤兒進(jìn)程。這個(gè)子進(jìn)程變成孤兒進(jìn)程的同時(shí)也變成了一個(gè)后臺(tái)進(jìn)程。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-842030.html
到了這里,關(guān)于【Linux】進(jìn)程排隊(duì)的理解&&進(jìn)程狀態(tài)的表述&&僵尸進(jìn)程和孤兒進(jìn)程的理解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!