??作者: 主頁(yè)
我的專欄 C語(yǔ)言從0到1 探秘C++ 數(shù)據(jù)結(jié)構(gòu)從0到1 探秘Linux 菜鳥刷題集 ??歡迎關(guān)注:??點(diǎn)贊??收藏??留言
??碼字不易,你的??點(diǎn)贊??收藏??關(guān)注對(duì)我真的很重要,有問(wèn)題可在評(píng)論區(qū)提出,感謝閱讀?。。?/strong>
前言
前面我們講述了進(jìn)程的基本概念以及如何描述、組織、查看進(jìn)程,如何使用fork函數(shù)創(chuàng)建子進(jìn)程等內(nèi)容,本篇將講述進(jìn)程的各種狀態(tài)。
先看看Linux內(nèi)核源代碼怎么說(shuō)
下面的狀態(tài)在kernel
源代碼里的定義:
/*
* 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èn)識(shí)進(jìn)程狀態(tài)
在我們講述Linux內(nèi)核
中的進(jìn)程狀態(tài)之前,我們先在看一下網(wǎng)上是怎么個(gè)樣子。
如下:
我們依次來(lái)解釋一下它的含義
新建
新建:字面意思,就是創(chuàng)建以后還沒有進(jìn)入運(yùn)行隊(duì)列(如fork后)
阻塞
阻塞:我們都知道進(jìn)程的代碼數(shù)據(jù)在內(nèi)存中,但是系統(tǒng)還有其他資源,如網(wǎng)卡、磁盤等…所以系統(tǒng)不止有一種隊(duì)列,不同的隊(duì)列速度不同,所以就會(huì)有時(shí)間差,阻塞狀態(tài)就是CPU等待非CPU資源就緒的狀態(tài)。
我們來(lái)驗(yàn)證一下:
比如有下面代碼:
#include<stdio.h>
int main()
{
int a = 0;
scanf("%d",&a);
return 0;
}
這個(gè)等待輸入的狀態(tài)就是阻塞狀態(tài)
運(yùn)行
比如之前講的task_struct結(jié)構(gòu)在隊(duì)列中排隊(duì)就叫做運(yùn)行態(tài)
掛起
和阻塞有點(diǎn)類似,但是它的區(qū)別是
掛起狀態(tài)會(huì)將進(jìn)程的代碼和數(shù)據(jù)換出到磁盤上
,而阻塞時(shí)的進(jìn)程代碼數(shù)據(jù)仍在內(nèi)存
。
終止
它是
X
和Z
兩種狀態(tài)的綜合體,具體之后再講~
進(jìn)程狀態(tài)查看命令
ps aux / ps axj 命令
Linux內(nèi)核中的進(jìn)程狀態(tài)
R
R即表示運(yùn)行態(tài),并不意味著進(jìn)程一定在運(yùn)行中,它表明進(jìn)程要么是在運(yùn)行中要么在運(yùn)行隊(duì)列里。
測(cè)試代碼:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
while(1)
{
}
return 0;
}
運(yùn)行結(jié)果:
S
阻塞態(tài),意味著進(jìn)程在等待事件完成(這里的睡眠有時(shí)候也叫做可中斷睡眠(interruptible sleep))。
我們都知道進(jìn)程的代碼數(shù)據(jù)在內(nèi)存中,但是系統(tǒng)還有其他資源,如網(wǎng)卡、磁盤等…所以系統(tǒng)不止有一種隊(duì)列,不同的隊(duì)列速度不同,所以就會(huì)有時(shí)間差,阻塞狀態(tài)就是CPU等待非CPU資源就緒的狀態(tài)。
如下圖:
這并不是真的S狀態(tài),為什么這么講?因?yàn)槟阍L問(wèn)了外設(shè),一旦訪問(wèn)外設(shè)的話,這個(gè)外設(shè)不一定說(shuō)是立馬給你準(zhǔn)備好,即便是給你準(zhǔn)備好了,其實(shí)你也要有一個(gè)讓他那么獲取你數(shù)據(jù)的過(guò)程。然后其實(shí)外設(shè)就比較慢,要等的話,其中你的進(jìn)程狀態(tài)大部分都是 s,所以你看到當(dāng)前,它的狀態(tài)就是 s 狀態(tài)。
D
磁盤休眠狀態(tài)(Disk sleep)有時(shí)候也叫不可中斷睡眠狀態(tài)(uninterruptible sleep),它不可被中斷、不可被動(dòng)喚醒,在這個(gè)狀態(tài)的進(jìn)程通常會(huì)等待IO的結(jié)束。
S與D的區(qū)別:
- 它們都是睡眠狀態(tài),幾乎等價(jià),但是S是可中斷的,D不可中斷。
那么D的意義是什么?在操作系統(tǒng)中,當(dāng)內(nèi)存不足時(shí),會(huì)自動(dòng)殺死一些不用的進(jìn)程,但是有些進(jìn)程一旦終止,便可能造成數(shù)據(jù)丟失,操作系統(tǒng)就需要知道哪些不能被自動(dòng)殺死,于是在S的基礎(chǔ)上就有了D狀態(tài)。
T
停止?fàn)顟B(tài)(stopped): 可以通過(guò)發(fā)送 SIGSTOP 信號(hào)給進(jìn)程來(lái)停止(T)進(jìn)程。這個(gè)被暫停的進(jìn)程可以通過(guò)發(fā)送 SIGCONT 信號(hào)讓進(jìn)程繼續(xù)運(yùn)行。
要注意和阻塞態(tài)的區(qū)別,它不用等待資源,比如打斷點(diǎn)時(shí)。
測(cè)試斷點(diǎn):上圖中的 t 就是暫停狀態(tài)。
X
死亡狀態(tài)(dead):這個(gè)狀態(tài)只是一個(gè)返回狀態(tài),你不會(huì)在任務(wù)列表里看到這個(gè)狀態(tài),它的作用是供給操作系統(tǒng)回收的,因?yàn)椴僮飨到y(tǒng)不會(huì)每時(shí)每刻都回收,而是過(guò)一段時(shí)間回收一次來(lái)提高效率。
到現(xiàn)在為止,我們對(duì)進(jìn)程的概念停留在兩個(gè)方面:
一、必須把自己的可執(zhí)行程序和代碼加載到內(nèi)存中。
二、操作系統(tǒng)為了管理眾多進(jìn)程,它必須給每個(gè)進(jìn)程都創(chuàng)建對(duì)應(yīng)的PCB。
接下來(lái)我們來(lái)講解兩種特殊的進(jìn)程,僵尸進(jìn)程
和孤兒進(jìn)程
。
Z
僵死狀態(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ǒng)來(lái)進(jìn)行回收。
只要子進(jìn)程退出,父進(jìn)程還在運(yùn)行,但父進(jìn)程沒有讀取子進(jìn)程狀態(tài),子進(jìn)程進(jìn)入Z狀態(tài)
我們可以這樣理解它,僵尸進(jìn)程可理解為他們的代碼和數(shù)據(jù)已經(jīng)被釋放掉,但PCB沒有釋放。
eg:假如有一個(gè)惡意程序。運(yùn)行后就是讓子進(jìn)程退出,但父進(jìn)程不回收他這樣就會(huì)導(dǎo)致僵尸問(wèn)題,讓操作系統(tǒng)速度卡頓
驗(yàn)證代碼:
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid=fork();
if(pid<0)
{
perror("fork");
return 1;
}
else if(pid==0)
{
while(1)
{
printf("I am child,pid:%d,ppid:%d\n",getpid(),getppid());
sleep(3);
break;
}
exit(0);
}
else
{
while(1)
{
printf("I am father,pid:%d,ppid:%d\n",getpid(),getppid());
sleep(1);
}
}
printf("you can see me!\n");
return 0;
}
運(yùn)行結(jié)果:
查看狀態(tài):
圖中的Z就是僵尸狀態(tài),子進(jìn)程已退出,但父進(jìn)程還在運(yùn)行,處于等待回收的被檢測(cè)狀態(tài)。
R+/S+/T+中的+是什么意思?
- 帶 + 為前臺(tái)任務(wù),一旦開始,就會(huì)占用bash對(duì)話框執(zhí)行命令就沒有任何效果了,而且可以被Ctrl + c 終止。
- 如果要讓它在后臺(tái)運(yùn)行,則使用
./mycode & [任務(wù)號(hào)] pid
,此時(shí)bash就看不到它的運(yùn)行了,變成了R狀態(tài),終止命令:kill -9 pid
,暫停命令:kill -19 pid
,繼續(xù)命令:kill -18 pid
僵尸進(jìn)程危害
進(jìn)程的退出狀態(tài)必須被維持下去,因?yàn)樗嬖V關(guān)心它的進(jìn)程(父進(jìn)程),你交給我的任務(wù),我辦的怎么樣了。
可父進(jìn)程如果一直不讀取,那子進(jìn)程就一直處于Z狀態(tài)?
是的!
維護(hù)退出狀態(tài)本身就是要用數(shù)據(jù)維護(hù),也屬于進(jìn)程基本信息,所以保存在task_struct(PCB)中,
換句話說(shuō),Z狀態(tài)一直不退出,PCB一直都要維護(hù)?是的!
那一個(gè)父進(jìn)程創(chuàng)建了很多子進(jìn)程,就是不回收,是不是就會(huì)造成內(nèi)存資源的浪費(fèi)?
是的!因?yàn)閿?shù)據(jù)結(jié)構(gòu)對(duì)象本身就要占用內(nèi)存,想想C語(yǔ)言中定義一個(gè)結(jié)構(gòu)體變量(對(duì)象),是要在內(nèi)存的某個(gè)位置進(jìn)行開辟空間!
是否會(huì)內(nèi)存泄漏?是的!比如。 C語(yǔ)言寫的程序中malloc后的資源不回收,此時(shí)導(dǎo)致的僵尸問(wèn)題就是內(nèi)存泄漏。
孤兒進(jìn)程
父進(jìn)程如果提前退出,那么子進(jìn)程后退出,進(jìn)入Z之后,那該如何處理呢?
父進(jìn)程先退出,子進(jìn)程就稱之為“孤兒進(jìn)程”
我們知道子進(jìn)程一般由父進(jìn)程回收,但是如果是上面這種情況,孤兒進(jìn)程會(huì)被1號(hào)init進(jìn)程領(lǐng)養(yǎng),當(dāng)然要由init進(jìn)程回收嘍。
為什么要被領(lǐng)養(yǎng)?
當(dāng)子進(jìn)程退出時(shí),父進(jìn)程已不在,需要由領(lǐng)養(yǎng)進(jìn)程來(lái)進(jìn)行回收。
驗(yàn)證一下:
#include<stdio.h>
#include <unistd.h>
int main()
{
pid_t id = fork();
if(id==0)
{
//child
while(1){}
}
else
{
int cnt=5;
while(cnt)
{
cnt--;
sleep(1);
}
}
return 0;
}
ps axj | head -1 && ps ajx | grep mycode
ps axj | head -1 && ps ajx | grep 2194
在一些較新的Linux發(fā)行版中,引入了--user選項(xiàng),該選項(xiàng)會(huì)創(chuàng)建一個(gè)用戶級(jí)的systemd實(shí)例,用于管理用戶級(jí)別的服務(wù)和進(jìn)程。
這個(gè)用戶級(jí)的systemd實(shí)例的進(jìn)程ID可能會(huì)出現(xiàn)在其他進(jìn)程的PPID字段中,包括孤兒進(jìn)程。
雖然孤兒進(jìn)程的父進(jìn)程通常是1號(hào)進(jìn)程,但在這種情況下,用戶級(jí)systemd實(shí)例的主進(jìn)程可能會(huì)被記錄為孤兒進(jìn)程的父進(jìn)程。
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-652747.html
后記
本篇從操作系統(tǒng)常見的進(jìn)程狀態(tài)來(lái)引入到Linux內(nèi)核中的進(jìn)程狀態(tài),他們中的狀態(tài)有所不同,但是其內(nèi)核是一樣的。比如R表示運(yùn)行態(tài)、S表示阻塞態(tài)、D表示磁盤休眠態(tài)、 T表示暫停狀態(tài)、X表示死亡狀、 G表示相似狀態(tài)、他們組合起來(lái),分別對(duì)應(yīng)的阻塞、運(yùn)行、掛起、終止這些狀態(tài)。我們還講述了在我們查看進(jìn)程狀態(tài)時(shí)看到的加號(hào)是什么意思?以及講述了兩種特殊的進(jìn)程狀態(tài),僵尸進(jìn)程和孤兒進(jìn)程。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-652747.html
到了這里,關(guān)于《Linux從練氣到飛升》No.13 Linux進(jìn)程狀態(tài)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!