目錄
? ? ? ??操作系統中
運行狀態
阻塞狀態
進程狀態轉換
?Linux系統中
查看進程狀態
深度睡眠狀態
T 暫停狀態
Z 僵尸狀態
?孤兒狀態
文章手稿
xmind:?
引言
介紹系統中的進程狀態及其管理方式。將通過結合操作系統原理和實際代碼示例,詳細說明進程的各種狀態、轉換過程以及處理方法。
操作系統中
一個進程通常有三種狀態
- 就緒狀態(Ready):表示進程已經具備運行所需要的一切條件,只需要等待CPU的分配就可以運行。進程處于就緒狀態時,通常會被添加到就緒隊列,等待調度器分配CPU資源。
- 運行狀態(Running):表示進程正在被CPU執行。處于運行狀態的進程正在使用CPU進行計算或其他操作。
- 阻塞狀態(Blocked):表示進程因為某些原因暫時無法繼續執行,需要等待一些特定條件的解除之后才能繼續運行。例如,當進程等待I/O操作完成或者等待某個資源可用時,會轉入阻塞狀態。進程在阻塞狀態時,通常會被移動到阻塞隊列中,等待條件的滿足。
我們下面將對運行,阻塞,和阻塞掛起進行介紹~?
運行狀態
?進程只要在運行隊列中,就叫做?運行態。
阻塞狀態
關于進程:
① 一個進程使用資源的時候,可不僅僅是在申請 CPU 資源
② 進程可能會申請其它資源:磁盤、網卡、顯卡,顯示器資源……如果我們申請 CPU 資源無法暫時無法得到滿足,這就需要排隊的 "運行隊列" 。那么如果我們申請其他慢設備的資源呢?是需要排隊的(task_struct 在進程排隊)。
當訪問某些資源(磁盤,網卡等),如果該資源暫時沒有準備好,或者正在給其他進程提供服務,那么此時:
① 當前進程要從 runqueue 中逐出。
② 將當前進程放入對應設備的描述結構體中的waitqueue 。
進程狀態:看PCB在哪個隊列
內存不足了,操作系統就會把 該進程的代碼和數據置換到磁盤上,進行?進程掛起。
進程狀態轉換
進程狀態的轉換可以通過以下示例說明:
#include <stdio.h>
#include <unistd.h>int main() {while (1) {printf("進程[%d]正在運行...\n", getpid());sleep(1); // 模擬阻塞狀態}return 0;
}
通過運行上述代碼并觀察進程狀態,可以理解進程在不同狀態之間的轉換過程。
三種狀態的圖示如下:
?Linux系統中
進程狀態用整數表示,這些整數存儲在進程的task_struct
結構體中。常見的進程狀態包括:運行(R)、睡眠(S)、磁盤睡眠(D)、停止(T)、死亡(X)、僵尸(Z)和孤兒進程。
進程狀態一覽
狀態代碼 | 狀態名稱 | 描述 |
---|---|---|
R | 運行(Running) | 進程正在運行或在運行隊列中等待 |
S | 睡眠(Sleeping) | 進程在等待某事件完成,可被信號喚醒 |
D | 磁盤睡眠(Disk Sleep) | 進程在等待I/O操作完成,不可被信號喚醒 |
T | 停止(Stopped) | 進程被暫停,可通過信號恢復 |
X | 死亡(Dead) | 進程已終止,從進程列表中移除 |
Z | 僵尸(Zombie) | 進程已退出,父進程尚未讀取其狀態 |
孤兒(Orphan) | 父進程已退出,被init進程收養 |
查看進程狀態
使用ps aux
或ps axj
命令可以查看系統中進程的狀態。例如:
ps aux
ps axj
這些命令輸出的狀態字段展示了進程當前的狀態。
背后的原因讓人暖心,cpu太快了,print顯示器等待的時間在他看來就是在sleep了
深度睡眠狀態
這個D狀態我們就不模擬了……可能會把我的機子磁盤打滿(害怕.dog)?
T 暫停狀態
比如看視頻,聽音樂,下載,都會有暫停。當你點擊暫停的時候下載對應的代碼就不跑了,此時這個進程你就可以認為是暫停狀態。
再比如說我們調試程序,讓程序打斷點之后讓程序運行起來,程序在打斷點處停住的時候是將進程暫停了,所以你在gdb 調試或在 VS 下調試時你會發現程序會停下來,這就是暫停。
是進程掛起的一種。
我們可以先來看一下kill
接下來可以來嘗試一下;
$ kill -19 4026,就會發現
gdb下的暫停狀態,測試一下
$ gdb process # 進入gdb調試
(gdb) l # 查看代碼
(gdb) b 9 # 打斷點
q + 回車 # 退出
Z 僵尸狀態
僵尸狀態:當一個 Linux 中的進程退出的時候,一般不會直接進入?X? 狀態(死亡,資源可以立馬被回收),而是進入?Z 狀態。
為什么呢~
進程為?Z 狀態,就是為了維護退出信息,可以讓父進程或者 OS 讀取記錄的,退出信息會寫入 test_struct。
以下是創建僵尸進程的代碼示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main() {pid_t id = fork();if (id < 0) {perror("fork");return 1;} else if (id == 0) { // 子進程printf("子進程[%d]開始運行...\n", getpid());sleep(5);printf("子進程[%d]退出...\n", getpid());exit(0);} else { // 父進程printf("父進程[%d]正在睡眠...\n", getpid());sleep(30); // 父進程延遲回收子進程}return 0;
}
?我們可以寫一個監控腳本來看一下~
while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep; sleep 1; echo "######" ; done
在另一個終端中運行ps
命令可以看到子進程進入僵尸狀態。
僵尸進程雖然不再運行,但它們仍然占用系統資源(如進程控制塊task_struct
)。如果父進程不及時回收子進程,會導致系統資源浪費,甚至內存泄漏。
可以通過以下方式:
- 父進程及時調用
wait()
或waitpid()
回收子進程。- 使用信號處理機制,在子進程退出時通知父進程進行回收。
?孤兒狀態
孤兒進程:父親沒了(bushi
即:父進程先退出了,子的父就變成1 號進程了,相當于被os領養了
測試一下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main(void) {pid_t id = fork();if (id == 0) {// childint cnt = 5;while (1) { // 死循環,孩子進程就不退了printf("我是子進程,我還剩下 %ds\n", cnt--);sleep(1);}printf("我是子進程,我已經變僵尸了,等待被檢測\n");exit(0);}else {// fatherint cnt = 3;while (cnt) {printf("我是父進程,我: %d\n", cnt--);sleep(1);}exit(0);}
}
?監控一下:
while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep; sleep 1;echo "######";done
我們可以top看一下1究竟是什么
?
? 疑問:父進程退出,為什么父進程沒有變成僵尸?我們怎么沒有看到父進程 為Z? ?
- 那是因為父進程的父進程是bash ,它會自動回收它的子進程,也就是這里的父進程。這里之所以沒有看到父進程變成僵尸,是因為被 bash 回收了, z->x 的狀態很快,所以你沒看到。
- 那為什么剛才我自己代碼中的父進程創建的子進程,父進程沒有回收子進程呢?那是因為你的代碼壓根就沒有寫回收,所以你的子進程就沒有回收。
那我們怎么暫停呢,ctrl+c 只能干掉前臺進程,
所以孤兒進程就要用到我們的殺進程:kill -9來暫停啦