Linux核心源代碼中,程序的狀态是用數字來表示的,為了弄明白正在運作的程序是什麼意思,我們需要知道程序的不同狀态。一個程序可以有幾個狀态(在Linux核心裡面,程序有時候也叫任務)
/*
* 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):這個狀态隻是一個傳回狀态,你不會在任務清單裡看到這個狀态。
1.程序各狀态的概念
1.1 R-運作态(running)
- 運作态是程序正在CPU上運作,還是程序隻要在運作隊列中就叫做運作态呢?
答:所謂的運作态,是隻要在運作隊列中就叫做運作态,代表我已經準備好了,随時可以被排程!
1.2 終止狀态(stopped)
- 終止狀态是這個程序已經被釋放,就叫做終止态?還是該程序還在,隻不過永遠不運作了,随時等待被釋放?
答:程序還在,隻不過不運作。
- 此時,我們想問,程序都終止了,為什麼不立馬釋放對應的資源嗎,而要維護一個終止态??
答:我們要釋放程序需要花費時間,有沒有可能,目前作業系統很忙呢。
1.3 運作阻塞
- 一個程序使用資源的時候,可不僅僅是在申請CPU資源!‘
- 程序可能申請更多的其他資源:磁盤,網卡,顯示卡,顯示器資源,聲霸卡/音響
如果我們申請CPU資源,暫時無法得到滿足,需要排隊的--運作隊列
那麼如果我們申請其他慢裝置的資源呢? --- 也是需要排隊的!(tast_struct在程序排隊)
當程序通路某些資源(磁盤,網卡),該資源如果暫時沒有準備好,或者正在給其他程序提供服務,此時:1.目前程序要從runqueue中移除;目前程序放入對應裝置的描述結構體中的等待隊列!(wait_queue)
當我們的程序此時在等待外部資源的時候,該程序的代碼不會被執行,此時程序處于的狀态叫做程序阻塞。
阻塞是一種臨時狀态。
1.4 運作挂起
- 之前我們提到過,代碼是存放在磁盤中的,當運作程式時,先将程式加載到記憶體,那麼當記憶體不足了怎麼辦?
當記憶體不足的時候,作業系統會将該程序的代碼進行輾轉騰挪,如何輾轉騰挪--短期内不會被排程的程序,他的代碼和資料依然在記憶體中!就是在白白浪費空間!作業系統就會把該程序的代碼和資料置換到磁盤上!此時被暫時置換到磁盤上的程序就叫做程序挂起。
此時我們将程序的狀态都一一介紹了,我們使用Linux作業系統再一一檢視一下
2.Linux程序狀态
2.1 R 運作态
- R運作狀态(running): 并不意味着程序一定在運作中,它表明程序要麼是在運作中要麼在運作隊列裡。
首先我們寫一段簡單的C語言代碼擷取pid
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("這是一個程序 %d\n",getpid());
sleep(1);
}
return 0;
}
我們輸入指令檢視
ps ajx | head -1 && ps ajx | grep 'test'|grep -v grep
發現運作的程序怎麼是S呢? 這是因為printf函數太快了,而顯示器顯示速度太慢啦,是以這個程序看似實在死循環,其實大多數時間都在等待顯示器。那麼怎麼才能進入運作态呢,我們屏蔽掉printf函數,隻保留while循環再次進行檢視發現已經處于運作态了
2.2 S 睡眠狀态
s對應的狀态一般叫做阻塞狀态!
如何把睡眠狀态叫醒呢?隻需要把程序狀态從S睡眠态轉換成R運作态即可。
我們常常把S狀态叫做淺度睡眠(也叫做可中斷睡眠),淺度睡眠可以被喚醒或者殺掉他。
隻要存在淺度睡眠,那麼一定存在深度睡眠,就是D狀态
2.3 D 深度睡眠
一般而言,Linux中如果我們等待的是磁盤資源,當程序等待磁盤資源拷貝寫入的過程,此時程序處于阻塞狀态,這種程序阻塞狀态就是D狀态(不可被中斷),作業系統也不能殺掉該程序,隻能等D狀态程序自己醒來。等待磁盤給回應才能醒來。D狀态和磁盤挂鈎,一般不好示範得出。我們有感性的了解即可。
2.4 Z 和 X 狀态
X(dead)狀态就是所謂的死亡狀态。
Z(zombie)狀态是僵屍狀态。Z狀态是一種已經死亡的狀态,但是死了之後,不要讓作業系統釋放它。那該狀态存在的意義是什麼呢?當一個Linux中的程序退出的時候,一般不會直接進入X狀态(死亡,資源可以立馬回收),而是進入Z狀态,為什麼?
因為程序被建立出來一定是要有任務完成,當程序退出的時候,我們怎麼知道程序把任務給我們完成了呢?需要将程序的執行結果告知給父程序或者作業系統。
子程序退出,維護Z狀态,就是為了讓父程序或者作業系統來讀取執行結果!父程序和作業系統通過程序等待來讀取僵屍程序的資訊。下圖是核心源碼中的退出資訊。
模拟僵屍程序?
建立子程序,子程序退出了,父程序不退出,也不等待子程序,子程序退出之後所處的狀态就是僵屍狀态。我們寫一段代碼來模拟一下!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t id = fork();
if(id == 0)
{
//child
int cnt = 5;
while(cnt)
{
printf("我是子程序,我還剩%dS\n",cnt--);
sleep(1);
}
printf("我是子程序,我已經僵屍了,等待被檢測\n");
exit(0);
}
else
{
//父
while(1)
{
sleep(1);
}
}
return 0;
}
我們在指令行寫一段腳本,循環列印程序狀态資訊
while :; do ps ajx | head -1 && ps ajx | grep 'test'|grep -v grep;
sleep 1; echo"#########################################################";done
我們發現一開始當子程序還沒有僵屍的時候,父子程序都處于S狀态
當子程序僵屍時,我們發現此時子程序的狀态變成了Z狀态
僵屍狀态後<defunct> 表示死者,死亡之意。
長時間僵屍,有什麼問題呢?
如果沒有人回收子程序的僵屍,該狀态會一直維護!該程序的相關資源(tast_struct)不會被釋放!會造成記憶體洩漏,是以一般必須要求父程序進行回收。
說到了僵屍程序,我們再談談與之相關的孤兒程序。
在我們們剛剛的代碼中,子程序先退出,而父程序一直存在,那如果父程序先退出,子程序一直存在,此時子程序的父程序已經被回收了,子程序沒有了父程序,我們把這種子程序處于的狀态叫做孤兒程序。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t id = fork();
if(id == 0)
{
//child
int cnt = 5;
while(1)
{
printf("我是子程序,我還剩%dS\n",cnt--);
sleep(1);
}
printf("我是子程序,我已經僵屍了,等待被檢測\n");
exit(0);
}
else
{
//f
int cnt = 3;
while(cnt--)
{
sleep(1);
}
}
return 0;
}
大約執行3秒過後,我們發現父程序不見了,之間子程序了,此時子程序就是孤兒程序,那麼孤兒程序要被1号程序領養。這個1号程序就是作業系統。
我們發現子程序狀态原先是S+,而後面為什麼變成了S呢?并且這段代碼不能被ctrl +c強制終止了
其中帶+号表示這個程序是前台程序,而前台程序能夠被ctrl C的,而不帶+的被稱為背景程序,我們要終止這個代碼,輸入kill -9 pid即可殺掉。
2.5 T狀态
在上面我們看到暫停狀态有T和t兩種狀态,這兩種狀态都叫暫停狀态,這兩種狀态沒有特别大的差別,而有一種特殊情況對應着t狀态。
暫停狀态就是讓運作的程序暫停一下,這個暫停狀态可以了解為我們下載下傳一個東西,我們讓他暫停一下;在看視訊的時候,暫停了一下。此時這個程序就處于暫停狀态。
我們在Linux下模拟一下暫停狀态,首先寫一段死循環程式
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("hello\n");
sleep(1);
}
}
當程式運作起來時,我們輸入
kill -19 pid
此時我們做運作的代碼被暫停了,此時程序所處的狀态就是暫停狀态,當我們想讓其繼續運作時,我們輸入
kill -18 pid
而t狀态時,當我們使用gdb調試代碼時,打上斷點後,我們r起來,我們發現了t狀态,t表示tracing,表示追蹤的意思,當程序被調試的時候,遇到斷點所處的狀态就是t狀态!