Linux程序狀態詳解

2021-07-31 14:38:07 字數 4512 閱讀 6992

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就這麼一兩個,程序動輒幾十上百個,一定是絕大多數程序都在睡眠。

linux程序狀態:d(task_uninterruptible),不可中斷的睡眠狀態

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

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

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

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

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

通過下面的**

#include

.h>

void main()

然後我們可以試一下task_uninterruptible的威力,不管是kill還是kill -9這個狀態的task_uninterruptible狀態的父程序都屹立不倒。但是我試驗的結果是vfork只能使得父程序不被kill殺死,但還是無法避免被kill -9殺死。

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等操作,或除錯程序退出,被除錯程序才能恢復task_running狀態。

linux程序狀態:z(task_dead-task-zombie),退出狀態,程序成為殭屍程序。

程序在退出的狀態中,處於task_dead狀態。

在這個退出過程中,程序占有的所有資源將被**,除了task_struct結構(以及少數資源)以外。於是程序就只剩下task_struct這個空殼,故稱為殭屍。

之所以保留task_stuct,是因為task_struct裡面儲存了程序的退出碼,以及一些統計資訊。而其父程序很有可能關心這些資訊。比如在shell中$!變數就儲存了最後乙個退出的前台程序的退出碼,而這個退出碼往往作為if語句的判斷條件。

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

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

只要父程序不退出,這個殭屍狀態的子程序就一直存在。那麼如果父程序退出了呢?誰來給子程序收屍?

當程序退出的時候會將它的所有子程序託管給別的程序(使之成為別的程序的子程序)。託管給誰呢?可能是退出程序所在程序組的下乙個程序(如果存在的話),或者是1號程序。所以每個程序每時每刻都有父程序存在,除非他是1號程序。

1號程序pid為1的程序,又稱init程序。

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

1、執行系統初始化指令碼,建立一系列程序。

2、在乙個死迴圈中等待子程序的退出事件,並呼叫waitid系統呼叫來完成「收屍」工作。

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

linux程序狀態:x(task_dead-exit_dead),退出狀態,程序即將被銷毀。

而程序在退出過程中也可能不會保留它的task_struct。比如這個程序是多執行緒程式中被detach的執行緒。或者父程序通過設定sigchld訊號的handler為sig_ign,顯式的忽略了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_struct結構加入到某個cpu的可執行佇列中,於是被喚醒的程序將有機會被排程執行。

而今晨從task_running狀態別為非task_running狀態,則有兩種途徑:

1.響應訊號而進入task_stopped狀態,或task_dead狀態。

2.執行系統呼叫主動進入task_interruptible狀態(如nanosleep系統呼叫)、或task_dead狀態(如exit系統呼叫);或由於執行系統呼叫的資源得不到滿足時,而進入task_interruptible或者task_uninterruptible狀態。

linux 程序 狀態

程序在執行過程中會根據環境來改變state。linux程序有以下狀態 running 程序處於執行 它是系統的當前程序 或者準備執行狀態 它在等待系統將cpu分配給它 waiting 程序在等待乙個事件或者資源。linux將等待程序分成兩類 可中斷與不可中斷。可中斷等待程序可以被訊號中斷 不可中斷等...

linux程序狀態

1.r task running,正在執行或者處於就緒狀態 很多作業系統教科書將正在cpu上執行的程序定義為running狀態 而將可執行但是尚未被排程執行的程序定義為ready狀態,這兩種狀態在linux下統一為 task running狀態。例子 ps aux 會看到ps這個程式命令的執行狀態是...

Linux程序狀態

在linux中,常見的程序狀態有以下幾種 task running 0 程序正在執行或者等待被排程執行 task interruptible 1 程序由於等待某個系統資源或者某個事件而處於掛起 睡眠 狀態。對其傳送訊號 signal 可以將其喚醒 進入task running狀態 task unin...