1. 前文鋪墊
進程狀態(tài)是task_struct內(nèi)的一個整數(shù);進行:進程在調(diào)度隊列中,進程的狀態(tài)都是running,阻塞:等待某種設(shè)備或者資源就緒。進程是一個隊列,設(shè)備也是一個隊列,當(dāng)我們讀磁盤,讀網(wǎng)卡的時候,如果對應(yīng)設(shè)備未就緒那么進程就要阻塞等待了。進程狀態(tài)變化的表現(xiàn)之一就是要在不同的隊列中進行流動,本質(zhì)都是數(shù)據(jù)結(jié)構(gòu)的增刪查改!
理解內(nèi)核鏈表
在這里插入圖片描述
如果一個類里面有多個next,prve,那么就可以把任何一個task_struct即屬于運行隊列,又屬于全局鏈表,還可以把它放到二叉樹中等。
在這里插入圖片描述
2. 進程狀態(tài)
一個進程可以有幾個狀態(tài)(在Linux內(nèi)核里,進程有時候也叫做任務(wù))。
下面的狀態(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. */ staticconstchar*consttask_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 運行或可運行 (Running 或 Runnable)
狀態(tài)描述:進程正在CPU上執(zhí)行,或在運行隊列中等待調(diào)度。
觸發(fā)場景:進程處于活動狀態(tài),正在執(zhí)行或準(zhǔn)備執(zhí)行。
S 可中斷睡眠(Interruptible Sleep)
狀態(tài)描述:進程在等待事件完成(如I/O操作、信號),可被信號中斷。
觸發(fā)場景:例如調(diào)用sleep()、read()等阻塞操作時。
D 不可中斷睡眠(Uninterruptible Sleep)
狀態(tài)描述:進程等待不可中斷的操作(如硬件I/O),不響應(yīng)信號。
觸發(fā)場景:常見于磁盤I/O或某些內(nèi)核操作,需等待操作完成。
T 停止(Stopped)
狀態(tài)描述:進程被信號(如SIGSTOP、SIGTSTP)暫停,需SIGCONT恢復(fù)。
觸發(fā)場景:手動暫停進程(如按Ctrl+Z)或調(diào)試時。
Z 僵尸(Zombie)
狀態(tài)描述:進程已終止,但父進程未調(diào)用wait()回收資源。
觸發(fā)場景:父進程未正確處理子進程退出,導(dǎo)致殘留進程描述符。
t 追蹤狀態(tài)(Tracing Stop)
狀態(tài)描述:進程被調(diào)試器(如gdb)跟蹤時暫停,屬于停止?fàn)顟B(tài)的一種。
觸發(fā)場景:調(diào)試器設(shè)置斷點或單步執(zhí)行時。
X 死亡(Dead)
狀態(tài)描述: 子進程結(jié)束之后,父進程獲取子進程信息之前。
觸發(fā)場景: 父進程已回收子進程狀態(tài),短暫存在后消失。
2.1 進程狀態(tài)查看
命令:ps aux / ps axj
a:用于顯示所有用戶的進程,包括其他用戶的進程(需要適當(dāng)?shù)臋?quán)限)。默認情況下,ps 只顯示當(dāng)前用戶的進程。
例如:ps a
x:用于顯示沒有控制終端的進程。這些進程通常是后臺運行的守護進程(daemon)。
例如:ps x
通常,ax選項一起使用,以顯示所有用戶的所有進程,無論它們是否有控制終端。
例如:ps ax
j:用于顯示與作業(yè)控制相關(guān)的信息,包括進程組ID(PGID)、會話ID(SID)、父進程ID(PPID),以及作業(yè)號(如果有的話)。
例如:ps j
這對于理解進程如何分組和作業(yè)控制很有幫助。
u :用于以用戶為中心的格式顯示進程信息。它提供了每個進程的詳細信息,如用戶、CPU使用率、內(nèi)存使用率、虛擬內(nèi)存大小、駐留內(nèi)存大小、控制終端、進程狀態(tài)、啟動時間、CPU時間和命令行。
例如:ps u
通常,u 選項與 aux 一起使用,以顯示所有用戶的詳細進程信息。
例如:ps aux
2.2 僵尸進程
僵死狀態(tài)(Zombies)是一個比較特殊的狀態(tài)。當(dāng)進程退出并且父進程(使用wait()系統(tǒng)調(diào)用)沒有讀取到子進程退出的返回代碼時就會產(chǎn)生僵死(尸)進程。
僵死進程會以終止?fàn)顟B(tài)保持在進程表中,并且會一直在等待父進程讀取退出狀態(tài)代碼。
只要子進程退出,父進程還在運行,但父進程沒有讀取子進程狀態(tài),子進程進入Z狀態(tài)。
2.3 僵尸進程危害
進程的退出狀態(tài)必須被維持下去,因為他要告訴關(guān)心它的進程(父進程),你交給我的任務(wù),我
辦的怎么樣了??筛高M程如果一直不讀取,那子進程就一直處于Z狀態(tài)?是的!
維護退出狀態(tài)本身就是要用數(shù)據(jù)維護,也屬于進程基本信息,所以保存在task_struct(PCB)中,換句話說,Z狀態(tài)?直不退出,PCB一直都要維護?是的!
那一個父進程創(chuàng)建了很多子進程,就是不回收,是不是就會造成內(nèi)存資源的浪費?是的!因為數(shù)據(jù)結(jié)構(gòu)對象本身就要占用內(nèi)存,C語言中定義一個結(jié)構(gòu)體變量(對象),是要在內(nèi)存的某個位置進行開辟空間,那就會存在內(nèi)存泄漏?是的!
如何避免呢?我們后期講。
2.4 孤兒進程
我們先來創(chuàng)建一段代碼
在這里插入圖片描述
代碼運行后,子進程一直運行,父進程運行5秒后退出
在這里插入圖片描述
這個1號進程是誰呢?top一下,我們可以看到它是systemd
在這里插入圖片描述
我們繼續(xù)查一下這個systemd
在這里插入圖片描述
為什么子進程會被1(systemd)號進程領(lǐng)養(yǎng)呢?如果不領(lǐng)養(yǎng)會出現(xiàn)什么問題呢?
答案是如果不被領(lǐng)養(yǎng),那么這個子進程就進入僵尸進程,有可能會造成內(nèi)存泄漏
父進程為什么不會變成孤兒進程或者僵尸進程呢?
答案是父進程也有自己的父進程,父進程的父進程就是bash
一旦進程變成孤兒進程,它就會被1號進程領(lǐng)養(yǎng),變成后臺進程,這時候Ctrl c就殺不掉它了,我們只能使用kill來殺死。
3. 進程優(yōu)先級
3.1 概念
cpu資源分配的先后順序,就是指進程的優(yōu)先級(priority)。
目標(biāo)資源稀缺,導(dǎo)致要通過優(yōu)先級確認誰先誰后的問題。
優(yōu)先權(quán)高的進程有優(yōu)先執(zhí)行權(quán)利。配置進程優(yōu)先級對多任務(wù)環(huán)境的linux很有用,可以改善系統(tǒng)性能。
還可以把進程運行到指定的CPU上,這樣一來,把不重要的進程安排到某個CPU,可以大大改善系統(tǒng)整體性能。
優(yōu)先級 vs 權(quán)限:優(yōu)先級是能得到資源,先后的問題,權(quán)限是能否得到資源的問題
優(yōu)先級其實也是一種數(shù)字,是task_struct中的一種屬性,數(shù)字值越低,優(yōu)先級越高 ,基于時間片的分時操作系統(tǒng),優(yōu)先級未來可能變化,但變化的幅度不能太大
3.2 查看系統(tǒng)進程
命令ps -al,其中a表示所有,l表示詳細信息。
我們上上面代碼中的父進程不再退出,父子進程一直運行,再運行代碼
在這里插入圖片描述
在linux系統(tǒng)中,每個用戶都有一個UID,linux中識別用戶就是用UID識別的。
UID :代表執(zhí)行者的身份
PID :代表這個進程的代號
PPID :代表這個進程是由哪個進程發(fā)展衍生出來的,亦即父進程的代號
PRI :代表這個進程可被執(zhí)行的優(yōu)先級,其值越小越早被執(zhí)行。進程優(yōu)先級默認:80
NI :代表這個進程優(yōu)先級的修正數(shù)據(jù),nice值
進程真實的優(yōu)先級 = PRI(默認) + NI
PRI(new) = PRI(old) + nice
所以,調(diào)整進程優(yōu)先級,在Linux下,就是調(diào)整進程nice值
nice 其取值范圍是-20至19,一共40個級別。linux進程優(yōu)先級范圍[60,99]
查UID,命令ls -ln
在這里插入圖片描述
sp用戶對應(yīng)的UID就是1001,文件創(chuàng)建的時候會把這個UID保存起來表明這個文件是誰創(chuàng)建的,進程創(chuàng)建的時候也會把UID保存起來表明進程是誰創(chuàng)建的。
所以當(dāng)我們訪問一個文件時,系統(tǒng)怎么識別出我們是擁有者,所屬組,或者other呢,我們訪問文件時本質(zhì)就是進程在訪問文件,進程怎么知道我們是誰呢?答案是是誰啟動的這個進程,進程就知道這個人的UID,這個文件是誰創(chuàng)建的這個文件的UID就有了,所以一個進程將來拿著它的UID和文件的UID做對比,相等了就是擁有者,不相等查下一個,兩個都不相等就是other。
linux系統(tǒng)中,訪問任何資源都是進程訪問,進程就代表用戶。
3.3 查看進程優(yōu)先級的命令
用top命令更改已存在進程的nice:
top
進入top后按“r”?>輸入進程PID?>輸入nice值
其他調(diào)整優(yōu)先級的命令:nice,renice
linux調(diào)整優(yōu)先級的系統(tǒng)調(diào)用
3.4 補充概念-競爭、獨立、并行、并發(fā)
競爭性: 系統(tǒng)進程數(shù)目眾多,而CPU資源只有少量,甚至1個,所以進程之間是具有競爭屬性的。為了高效完成任務(wù),更合理競爭相關(guān)資源,便具有了優(yōu)先級
獨立性: 多進程運行,需要獨享各種資源,多進程運行期間互不干擾
并行: 多個進程在多個CPU下分別,同時進行運行,這稱之為并行
并發(fā): 多個進程在?個CPU下采用進程切換的方式,在一段時間之內(nèi),讓多個進程都得以推進,稱之為并發(fā)
在這里插入圖片描述
4. 進程切換
先談兩個問題:
死循環(huán)進程是如何運行的
我們平時在vs中寫一個while(1)的死循環(huán),一旦跑起來我們就會發(fā)現(xiàn)系統(tǒng)會變卡了,但是不會卡死。
a.一旦一個進程占有CPU,會把自己的代碼跑完嗎?不會!(除非這個代碼很短)每個進程系統(tǒng)都會為它分配一個叫做時間片的東西。所以每一個進程擁有CPU資源都不是永久性的,而是臨時性的。
b.死循環(huán)進程不會打死進程,因為死循環(huán)進程不會一直占用CPU!
cpu,寄存器
cpu執(zhí)行一個進程的時候就和PCB的關(guān)系不大了,cpu重點是訪問的是進程的代碼和數(shù)據(jù),所以cpu會訪問當(dāng)前進程的代碼和數(shù)據(jù),為了能夠處理一條一條的代碼和數(shù)據(jù),所以cpu中會存在很多的寄存器,每個寄存器在cpu內(nèi)部都有著臨時保存數(shù)據(jù)的任務(wù),所以當(dāng)進程再跑時,寄存器就會被填上臨時值,有的是計算結(jié)果,浮點數(shù)計算有沒有錯誤等。
結(jié)論:a.寄存器就是cpu內(nèi)部的臨時空間 b.寄存器 != 寄存器里面的數(shù)據(jù)
進程如何切換?
CPU上下文切換:其實際含義是任務(wù)切換,或者CPU寄存器切換。當(dāng)多任務(wù)內(nèi)核決定運行另外的任務(wù)時,它保存正在運行任務(wù)的當(dāng)前狀態(tài),也就是CPU寄存器中的全部內(nèi)容。這些內(nèi)容被保存在任務(wù)自己的堆棧中,入棧工作完成后就把下一個將要運行的任務(wù)的當(dāng)前狀況從該任務(wù)的棧中重新裝入CPU寄存器,并開始下一個任務(wù)的運行,這一過程就是context switch。
在這里插入圖片描述
進程切換最核心的就是保存和恢復(fù)當(dāng)前進程的硬件上下文數(shù)據(jù),即cpu內(nèi)寄存器的內(nèi)容。
保存在哪里?
保存到進程的task_struct里面
如何區(qū)分新的進程和已經(jīng)調(diào)度過的進程?
在task_struct中增加一個標(biāo)記位。
5.Linux2.6內(nèi)核進程O(1)調(diào)度隊列
在這里插入圖片描述
一個CPU擁有一個runqueue
如果有多個CPU就要考慮進程個數(shù)的負載均衡問題
優(yōu)先級
普通優(yōu)先級:100?139(我們都是普通的優(yōu)先級,想想nice值的取值范圍,可與之對應(yīng)!)
實時優(yōu)先級:0?99(不關(guān)心)
活動隊列
時間片還沒有結(jié)束的所有進程都按照優(yōu)先級放在該隊列
nr_active :總共有多少個運行狀態(tài)的進程
queue[140] :一個元素就是一個進程隊列,相同優(yōu)先級的進程按照FIFO規(guī)則進行排隊調(diào)度,所以,數(shù)組下標(biāo)就是優(yōu)先級!
從該結(jié)構(gòu)中,選擇一個最合適的進程,過程是怎么的呢?
a. 從0下標(biāo)開始遍歷queue[140]
b. 找到第一個非空隊列,該隊列必定為優(yōu)先級最高的隊列
c. 拿到選中隊列的第一個進程,開始運行,調(diào)度完成!
d. 遍歷queue[140]時間復(fù)雜度是常數(shù)!但還是太低效了!
bitmap[5] :一共140個優(yōu)先級,一共140個進程隊列,為了提高查找非空隊列的效率,就可以用5*32個比特位表示隊列是否為空,這樣,便可以大 大提高查找效率。
在這里插入圖片描述
過期隊列
過期隊列和活動隊列結(jié)構(gòu)一模一樣
過期隊列上放置的進程,都是時間片耗盡的進程
當(dāng)活動隊列上的進程都被處理完畢之后,對過期隊列的進程進行時間片重新計算
active指針和expired指針
active 指針永遠指向活動隊列
expired 指針永遠指向過期隊列
活動隊列上的進程會越來越少,過期隊列上的進程會越來越多,因為進程時間片到期時一直都存在的。
在合適的時候,只要能夠交換active指針和expired指針的內(nèi)容,就相當(dāng)于有具有了一批新的活動進程!
linux真是算法調(diào)度:O(1)調(diào)度算法
再次理解nice值:nice值是為了保證老進程的優(yōu)先級不被強制改變,原本進程的優(yōu)先級不改變,加上一個nice值,當(dāng)本次調(diào)度完重新放入過期隊列時,更新優(yōu)先級,鏈入到指定位置。
-
內(nèi)核
+關(guān)注
關(guān)注
3文章
1416瀏覽量
41444 -
Linux
+關(guān)注
關(guān)注
87文章
11511瀏覽量
213845 -
指針
+關(guān)注
關(guān)注
1文章
484瀏覽量
71198
原文標(biāo)題:Linux進程狀態(tài)(僵尸進程,孤兒進程),優(yōu)先級與調(diào)度機制
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
Linux系統(tǒng)下進程的幾種狀態(tài)介紹

Linux設(shè)備驅(qū)動開發(fā)詳解
【Linux學(xué)習(xí)雜談】之進程狀態(tài)
Linux下的進程結(jié)構(gòu)
詳解如何監(jiān)控和保護Linux下進程安全
你知道Linux進程的睡眠和喚醒操作?

Linux 進程狀態(tài)淺析
ipcs命令:用于查看Linux進程間通信設(shè)施的狀態(tài)
你們知道Linux的進程是怎樣創(chuàng)建的嗎

評論