Linux程序的幾種狀態

2021-06-01 05:17:02 字數 4802 閱讀 1485

linux是乙個多使用者,多工的系統,可以同時執行多個使用者的多個程式,就必然會產生很多的程序,而每個程序會有不同的狀態。

linux程序狀態:r (task_running),可執行狀態。 只有在該狀態的程序才可能在cpu上執行。而同一時刻可能有多個程序處於可執行狀態,這些程序的task_struct結構(程序控制塊)被放入對應 cpu的可執行佇列中(乙個程序最多只能出現在乙個cpu的可執行佇列中)。程序排程器的任務就是從各個cpu的可執行佇列中分別選擇乙個程序在該 cpu上執行。

很多作業系統教科書將正在cpu上執行的程序定義為running狀態、而將可執行但是尚未被排程執行的程序定義為ready狀態,這兩種狀態在linux下統一為 task_running狀態。

linux程序狀態:s (task_interruptible),可中斷的睡眠狀態。 處於這個狀態的程序因為等待某某事件的發生(比如等待socket連線、等待訊號量),而被掛起。這些程序的task_struct結構被放入對應事件的等待佇列中。當這些事件發生時(由外部中斷觸發、或由其他程序觸發),對應的等待佇列中的乙個或多個程序將被喚醒。

通過ps命令我們會看到,一般情況下,程序列表中的絕大多數程序都處於task_interruptible狀態(除非機器的負載很高)。畢竟cpu就這麼一兩個,程序動輒幾十上百個,如果不是絕大多數程序都在睡眠,cpu又怎麼響應得過來。

linux程序狀態:d (task_uninterruptible),不可中斷的睡眠狀態。 與task_interruptible狀態類似,程序處於睡眠狀態,但是此刻程序是不可中斷的。不可中斷,指的並不是cpu不響應外部硬體的中斷,而是指程序不響應非同步訊號。

絕大多數情況下,程序處在睡眠狀態時,總是應該能夠響應非同步訊號的。否則你將驚奇的發現,kill -9竟然殺不死乙個正在睡眠的程序了!於是我們也很好理解,為什麼ps命令看到的程序幾乎不會出現task_uninterruptible狀態,而總是 task_interruptible狀態。

而task_uninterruptible狀態存在的意義就在於,核心的某些處理流程是不能被打斷的。如果響應非同步訊號,程式的執行流程中就會被插入一段用於處理非同步訊號的流程(這個插入的流程可能只存在於核心態,也可能延伸到使用者態),於是原有的流程就被中斷了。(參見《linux核心非同步中斷**》)

在程序對某些硬體進行操作時(比如程序呼叫read系統呼叫對某個裝置檔案進行讀操作,而read系統呼叫最終執行到對應裝置驅動的**,並與對應的物理裝置進行互動),可能需要使用task_uninterruptible狀態對程序進行保護,以避免程序與裝置互動的過程被打斷,造成裝置陷入不可控的狀態。這種情況下的task_uninterruptible狀態總是非常短暫的,通過ps命令基本上不可能捕捉到。

linux系統中也存在容易捕捉的task_uninterruptible狀態。執行vfork系統呼叫後,父程序將進入task_uninterruptible狀態,直到子程序呼叫exit或exec(參見《神奇的vfork》)。

通過下面的**就能得到處於task_uninterruptible狀態的程序

#include

int main(void)

編譯執行,然後ps一下:

$ ps -ax | grep a\.out

4371 pts/0 d+ 0:00 ./a.out

4372 pts/0 s+ 0:00

./a.out 4374 pts/1 s+ 0:00 grep a.out

然後我們可以試驗一下task_uninterruptible狀態的威力。不管kill還是kill -9,這個task_uninterruptible狀態的父程序依然屹立不倒。

linux程序狀態:t (task_stopped or task_traced),暫停狀態或跟蹤狀態。 向程序傳送乙個sigstop訊號,它就會因響應該訊號而進入task_stopped狀態(除非該程序本身處於 task_uninterruptible狀態而不響應訊號)。(sigstop與sigkill訊號一樣,是非常強制的。不允許使用者程序通過 signal系列的系統呼叫重新設定對應的訊號處理函式。)

向程序傳送乙個sigcont訊號,可以讓其從task_stopped狀態恢復到task_running狀態。

當程序正在被跟蹤時,它處於task_traced這個特殊的狀態。「正在被跟蹤」指的是程序暫停下來,等待跟蹤它的程序對它進行操作。比如在 gdb中對被跟蹤的程序下乙個斷點,程序在斷點處停下來的時候就處於task_traced狀態。而在其他時候,被跟蹤的程序還是處於前面提到的那些狀態。

對於程序本身來說,task_stopped和task_traced狀態很類似,都是表示程序暫停下來。

而task_traced狀態相當於在task_stopped之上多了一層保護,處於task_traced狀態的程序不能響應 sigcont訊號而被喚醒。只能等到除錯程序通過ptrace系統呼叫執行ptrace_cont、ptrace_detach等操作(通過 ptrace系統呼叫的引數指定操作),或除錯程序退出,被除錯的程序才能恢復task_running狀態。

linux程序狀態:z (task_dead - exit_zombie),退出狀態,程序成為殭屍程序。 程序在退出的過程中,處於task_dead狀態。在這個退出過程中,程序占有的所有資源將被**,除了task_struct結構(以及少數資源)以外。於是程序就只剩下task_struct這麼個空殼,故稱為殭屍。之所以保留task_struct,是因為task_struct裡面儲存了程序的退出碼、以及一些統計資訊。而其父程序很可能會關心這些資訊。比如在shell中,$?變數就儲存了最後乙個退出的前台程序的退出碼,而這個退出碼往往被作為if語句的判斷條件。

當然,核心也可以將這些資訊儲存在別的地方,而將task_struct結構釋放掉,以節省一些空間。但是使用task_struct結構更為方便,因為在核心中已經建立了從pid到task_struct查詢關係,還有程序間的父子關係。釋放掉task_struct,則需要建立一些新的資料結構,以便讓父程序找到它的子程序的退出資訊。

父程序可以通過wait系列的系統呼叫(如wait4、waitid)來等待某個或某些子程序的退出,並獲取它的退出資訊。然後wait系列的系統呼叫會順便將子程序的屍體(task_struct)也釋放掉。子程序在退出的過程中,核心會給其父程序傳送乙個訊號,通知父程序來「收屍」。這個訊號預設是sigchld,但是在通過clone系統呼叫建立子程序時,可以設定這個訊號。

通過下面的**能夠製造乙個exit_zombie狀態的程序:

#include

int main(void)

編譯執行,然後ps一下:

$ ps -ax | grep a\.out 10410 pts/0 s+ 0:00 ./a.out 10411 pts/0 z+ 0:00 [a.out] 10413 pts/1 s+ 0:00 grep a.out

只要父程序不退出,這個殭屍狀態的子程序就一直存在。那麼如果父程序退出了呢,誰又來給子程序「收屍」?當程序退出的時候,會將它的所有子程序都託管給別的程序(使之成為別的程序的子程序)。託管給誰呢?可能是退出程序所在程序組的下乙個程序(如果存在的話),或者是1號程序。所以每個程序、每時每刻都有父程序存在。除非它是1號程序。

1號程序,pid為1的程序,又稱init程序。linux系統啟動後,第乙個被建立的使用者態程序就是init程序。它有兩項使命:

init程序不會被暫停、也不會被殺死(這是由核心來保證的)。它在等待子程序退出的過程中處於task_interruptible狀態,「收屍」過程中則處於task_running狀態。

linux程序狀態:x (task_dead - exit_dead),退出狀態,程序即將被銷毀。 而程序在退出過程中也可能不會保留它的task_struct。比如這個程序是多執行緒程式中被detach過的程序(程序?執行緒?參見《linux執行緒**》)。或者父程序通過設定sigchld訊號的handler為sig_ign,顯式的忽略了sigchld訊號。(這是posix 的規定,儘管子程序的退出訊號可以被設定為sigchld以外的其他訊號。)此時,程序將被置於exit_dead退出狀態,這意味著接下來的**立即就會將該程序徹底釋放。所以exit_dead狀態是非常短暫的,幾乎不可能通過ps命令捕捉到。

程序是通過fork系列的系統呼叫(fork、clone、vfork)來建立的,核心(或核心模組)也可以通過kernel_thread函式建立核心程序。這些建立子程序的函式本質上都完成了相同的功能——將呼叫程序複製乙份,得到子程序。(可以通過選項引數來決定各種資源是共享、還是私有。)那麼既然呼叫程序處於task_running狀態(否則,它若不是正在執行,又怎麼進行呼叫?),則子程序預設也處於task_running狀態。另外,在系統呼叫呼叫clone和核心函式kernel_thread也接受clone_stopped選項,從而將子程序的初始狀態置為 task_stopped。

程序自建立以後,狀態可能發生一系列的變化,直到程序退出。而儘管程序狀態有好幾種,但是程序狀態的變遷卻只有兩個方向——從task_running狀態變為非task_running狀態、或者從非task_running狀態變為task_running狀態。也就是說,如果給乙個task_interruptible狀態的程序傳送sigkill訊號,這個程序將先被喚醒(進入 task_running狀態),然後再響應sigkill訊號而退出(變為task_dead狀態)。並不會從task_interruptible狀態直接退出。程序從非task_running狀態變為task_running狀態,是由別的程序(也可能是中斷處理程式)執行喚醒操作來實現的。執行喚醒的程序設定被喚醒程序的狀態為task_running,然後將其task_struct結構加入到某個cpu的可執行佇列中。於是被喚醒的程序將有機會被排程執行。

而程序從task_running狀態變為非task_running狀態,則有兩種途徑:

顯然,這兩種情況都只能發生在程序正在cpu上執行的情況下。

程序的幾種狀態

程序 程序 process 是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位。程序狀態 乙個程序的生命週期可以劃分為一組狀態,這些狀態刻畫了整個程序。程序狀態即體現乙個程序的生命狀態。程序狀態 一般來說,程序有三個狀態,即就緒狀態,執行狀態 阻塞狀態。執行態 程序占...

程序的幾種狀態

程序的幾種狀態 1 執行狀態 程序正在處理器上上執行。在單處理器環境下,每個時刻最多只有乙個程序處於執行狀態。2 就緒狀態 程序已處於準備執行狀態,即程序獲得了除了處理器之外的一切所需資源,一旦得到處理器即可執行。3 阻塞狀態 又稱為等待狀態,程序正在等待某一事件而暫停執行,如等待某資源為可用 不包...

Linux中程序有哪幾種狀態

只有在該狀態的程序才可能在cpu上執行。而同一時刻可能有多個程序處於可執行狀態,這些程序的task struct結構 程序控制塊 被放入對應cpu的可執行佇列中 乙個程序最多只能出現在乙個cpu的可執行佇列中 程序排程器的任務就是從各個cpu的可執行佇列中分別選擇乙個程序在該cpu上執行。很多作業系...