目錄
一、進程狀態
1.1 什么是進程狀態
1.2 運行狀態
1.2 阻塞狀態
1.3 掛起狀態
二、Linux操作系統上具體的進程狀態
2.1 狀態
2.2 R 和 S 狀態的查看
2.3 后臺進程和前臺進程
2.4 休眠狀態和深度休眠狀態
一、進程狀態
1.1 什么是進程狀態
? ? ? ?首先我們知道我們的操作系統是通過我們的PCB來管理我們的進程的,那么我們的狀態這個屬性一定在我們的PCB這個結構體里(其在PCB中是一個整形字段)。用簡單的話來說,用宏定義(也可能是其他方式)了幾個值,用這幾個值來代表我們不同的狀態。
#define NEW 1
#define RUNNING 2
#define BLOCK 3struct PCB
{...//其他屬性int status;//狀態
}//通過改變status的值來代表改變進程的狀態
根據一些教材上的描述,進程大概會有以下的一些狀態。
(圖片來源于網絡)。
? ? ? ?接下來我就來給大家好好介紹一下這些狀態(其中創建狀態、就緒狀態和運行狀態我統一歸結為運行狀態)。
1.2 運行狀態
? ? ? ?在上文回答第四個問題的時候,我有提到過一個運行隊列的概念,它提供了一種有序執行任務的機制,使得任務的執行順序可控,并能夠有效地利用系統資源。所以我們的OS會維護一個運行隊列去存放我們的進程,而我們的CPU則會去執行該隊列PCB所指的代碼。
由此,我們的出一個概念:
不管一個進程是否在被處理,只要其PCB加入了運行隊列,我們就稱其處在運行狀態。
1.2 阻塞狀態
? ? ? ?我們的代碼一定會或多或少的會訪問系統中的某些資源,比方說:鍵盤、硬盤等等,在比方說我們的scanf()和cin>> ,本質上都是從我們的鍵盤上讀取數據,那要是我們一直不輸入怎么辦,那是不是我們的程序會一直卡在那,不動了。為什么會不動呢,因為需要的數據沒有就緒,也就是我們進程要訪問的資源沒有就緒,條件不具備,我們的代碼就沒辦法往下執行。
? ? ? ?我們的OS要管理我們的進程,也同樣會去管理我們的硬件資源,也就是說我們設備的資源充不充足,有沒有就緒,OS是知道的,怎么知道的呢,通過維護我們的硬件資源的dev_list。但這個
list多個一個屬性:PCB* wait_queue。也就是說,當我們的設備資源不充足時,我們對應的PCB就會加入到該設備的這個等待隊列中,而我們把在設備的等待隊列中的PCB的狀態叫做阻塞狀態。
通過這部分的講解,我們可以得到一個結論:
進程狀態變化的本質:
1.更改PCB 對應的status變量的值
2.將PCB鏈入到我們不同的隊列當中
1.3 掛起狀態
? ? ? ?如果一個進程當前被阻塞了,那就注定了這個進程在其所需要的資源沒有就緒之前是不會被調度的。那么如果這個時候我們的操作系統內的內存資源嚴重不足了該怎么辦?
? ? ? ?我們的操作系統在我們的磁盤中劃分了一個叫做swap的分區,其作用就是在我們OS的內存資源不足的時候,換取一些資源回來。怎么換呢?換誰呢?就換我們處于阻塞狀態的進程。
?
? ? ? ?將我們的PCB的數據(是數據置換了,留出空間,而不是這個進程沒有了)置換到我們的swap分區,置換之后我們的進程所處狀態就叫掛起狀態。
? ? ? ?這個時候可能有人會問,OS不是非常注重效率的嗎,其主動去訪問我們的磁盤,不會降低我們OS的效率嗎?確實會影響我們OS的效率,但是這個時候OS都快掛掉了,所以優先考慮的問題是讓OS運行下去。
? ? ? ?當之后我們的資源空閑出來,我們的進程被重新調度時,曾經被置換出去的數據和代碼,又會被重新置換回來。
二、Linux操作系統上具體的進程狀態
2.1 狀態
先來看看再kernal內核里的代碼
/*
* 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運行狀態(running): 并不意味著進程一定在運行中,它表明進程要么是在運行中要么在運行隊列里。
- S睡眠狀態(sleeping): 意味著進程在等待事件完成(這里的睡眠有時候也叫做可中斷睡眠(interruptible sleep))。
- D磁盤休眠狀態(Disk sleep)有時候也叫不可中斷睡眠狀態(uninterruptible sleep),在這個狀態的進程通常會等待IO的結束。
- T停止狀態(stopped): 可以通過發送 SIGSTOP 信號給進程來停止(T)進程。這個被暫停的進程可以通過發送 SIGCONT 信號讓進程繼續運行。
- X死亡狀態(dead):這個狀態只是一個返回狀態,你不會在任務列表里看到這個狀態。
2.2 R 和 S 狀態的查看
我們可以通過我們的代碼和我們的指令來查看我們的進程狀態,先寫一段代碼。
可以看到我們的狀態如下
? ? ? 其中STAT(status)就代表我們的狀態欄,可以看到我們的程序,一直明明在跑,卻處于我們的S(即阻塞狀態)狀態,這是為什么呢?其實我們的CPU速度是很快的,程序其實已經走完了,但是我們的printf語句是需要訪問我們的外設的(顯示器),訪問外設又是一個比較慢的過程,所以該進程的大部分時間都處于S狀態。那如果我們不輸出呢?
可以看到我們的進程狀態就可以被觀測到處于我們的R狀態(運行狀態)。至于這個+號是什么意思,我們馬上就能知道了。
2.3 后臺進程和前臺進程
有+號代表是前臺進程,沒有的代表是后臺進程。
首先這兩個概念是什么意思呢?
前臺進程:進程在被執行時,無法使用其他的指令,且其可以被ctrl + C 強行終止掉
后臺進程:進程在被執行時,可以使用其他的指令,但是不能被ctrl + C 強行終止掉,所以需要kill將其殺掉。
那怎么將我們的進程變成一個后臺進程呢?
在執行我們的程序時,在其后面加上一個 &
類似于: ./mybin &
給大家演示一下:
? ? ? 可以看到我們在邊執行程序的時候還可以使用我們的指令(如果要終止這個進程使用指令 kill -9 + 該進程的PID)。
2.4 休眠狀態和深度休眠狀態
? ? ? ?對于這個狀態給大家舉個例子就能理解了,假設我們有一個進程正在向磁盤中存放數據(數據量有點大),由于訪問我們的硬件速度很慢,我們的進程就會進入我們的S狀態(即阻塞狀態),如果這個時候我內存資源已經不夠,swap分區也不夠用了,需要干掉一些進程來存活,那恰好就把這個還在等待磁盤返回結果的進程干掉了,其數據全都釋放了。那此時,如果我們的磁盤存儲失敗了,那我們的這部分數據就丟失了,那是不是很容易造成很嚴重的影響。所以就有了我們深度睡眠狀態D,不可被中斷。
D磁盤休眠狀態(Disk sleep)有時候也叫不可中斷睡眠狀態(uninterruptible sleep),在這個狀態的進程通常會等待IO的結束。