1. 程式人生 > >linux程序狀態詳解

linux程序狀態詳解

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狀態的程序:

  1. #include
  2. void main() {
  3. if (!vfork()) sleep(100);
  4. }

編譯執行,然後ps一下:

  1. [email protected]kouu-one:~/test$ ps -ax | grep a\.out
  2. 4371 pts/0    D+     0:00 ./a.out
  3. 4372 pts/0    S+     0:00 ./a.out
  4. 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狀態的程序:

  1. #include
  2. void main() {
  3. if (fork())
  4. while(1) sleep(100);
  5. }

編譯執行,然後ps一下:

  1. [email protected]:~/test$ ps -ax | grep a\.out
  2. 10410 pts/0    S+     0:00 ./a.out
  3. 10411 pts/0    Z+     0:00 [a.out]
  4. 10413 pts/1    S+     0:00 grep a.out

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

1號程序,pid為1的程序,又稱init程序。
linux系統啟動後,第一個被建立的使用者態程序就是init程序。它有兩項使命:
1、執行系統初始化指令碼,建立一系列的程序(它們都是init程序的子孫);
2、在一個死迴圈中等待其子程序的退出事件,並呼叫waitid系統呼叫來完成“收屍”工作;
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狀態,則有兩種途徑:
1、響應訊號而進入TASK_STOPED狀態、或TASK_DEAD狀態;
2、執行系統呼叫主動進入TASK_INTERRUPTIBLE狀態(如nanosleep系統呼叫)、或TASK_DEAD狀態(如exit系統呼叫);或由於執行系統呼叫需要的資源得不到滿足,而進入TASK_INTERRUPTIBLE狀態或TASK_UNINTERRUPTIBLE狀態(如select系統呼叫)。

相關推薦

linux程序狀態

Linux是一個多使用者,多工的系統,可以同時執行多個使用者的多個程式,就必然會產生很多的程序,而每個程序會有不同的狀態。 Linux程序狀態:R (TASK_RUNNING),可執行狀態。 只有在該狀態的程序才可能在CPU上執行。而同一時刻可能有多個程序處於可執行狀

LinuxLinux程序訊號

一、引入訊號概念訊號其實我們也見過,當我們在shell上寫出一個死迴圈退不出來的時候,只需要一個組合鍵,ctrl+c,就可以解決了,這就是一個訊號,但是真正的過程並不是那麼簡單的。1、當用戶按下這一對組合鍵時,這個鍵盤輸入會產生一個硬體中斷,如果CPU正在執行這個程序的程式碼

[linux]vmstat命令-顯示虛擬內存狀態

span 並不是 增加 OS 表現 如果 2個 blog IT 本文轉載於http://man.linuxde.net/vmstat vmstat命令的含義為顯示虛擬內存狀態(“Viryual Memor Statics”),但是它可以報告關於進程、內存、I/O等系統整

linux程序程式設計

1.建立程序fork() 1.1標頭檔案 #include<unistd.h> #include<sys/types.h> 1.2函式原型 pid_t fork( void); pid_t 是一個巨集定義,其實質是int 被定義在#i

linux檢視程序命令(ps命令)

  Linux中的ps命令是Process Status的縮寫。ps命令用來列出系統中當前執行的那些程序。ps命令列出的是當前那些程序的快照,就是執行ps命令的那個時刻的那些程序,如果想要動態的顯示程序資訊,就可以使用top命令。要對程序進行監測和控制,首先必須要了解當前

Linux程式設計】守護程序(daemon)與建立

一、概述   Daemon(守護程序)是執行在後臺的一種特殊程序。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。它不需要使用者輸入就能執行而且提供某種服務,不是對整個系統就是對某個使用者程式提供服務。Linux系統的大多數伺服器就是通過守護程序實現的。常見的守護

linux檢視程序例項

檢視程序 ps (重要) Process State 是非常強大的程序檢視命令,用於報告當前系統的程序狀態。 常用選項: 1、BSD風格  a——和終端有關的程序  x——和終端無關的程序  u——顯示程序的執行者  2、sys V風格  -e——檢視所有程序  -l——l

Linux檢視和結束程序命令

轉自:http://www.360doc.com/content/14/1021/20/14508903_418760439.shtml 先看兩個例子:  例子一:結束某個程式,如Firefox        鍵入命令: pkill  firefox  例子二:結束某個程

[測試]Linux檢視和結束程序命令

-------------------------------------------------------------------先看兩個例子: 例子一:結束某個程式,如Firefox      鍵入命令:pkill firefox 例子二: 結束某個程序,如find            先用ps命

linux核心之程序管理

1、程序描述符 (1)程序與執行緒          程序是處於執行期的程式以及相關資源的總稱。執行緒在linux上稱為輕量級程序,沒有獨立的地址空間,一個程序下的所有執行緒共享地址空間、檔案系統資源、檔案描述符、訊號處理程式等。 (2)程序描述符task_struct

Linux 程序程序排程

1、引言 程序:是程式執行時的一個例項,可以看作充分描述程式已經執行到何種程度的資料結構的彙集。從核心觀點看,程序的目的就是擔當分配系統資源(CPU時間、記憶體等)的實體。 當一個程序建立時,它獲得一個父程序地址空間的副本。共享正文段(程式碼段),但並不執行

LinuxLinux 下多程序程式設計

一.多程序程式的特點    程序是一個具有獨立功能的程式關於某個資料集合的一次可以併發執行的執行活動,是處 於活動狀態的計算機程式。    程序作為構成系統的基本細胞, 不僅是系統內部獨立執行的實體, 而且是獨立競爭資源的基本實體。    程序

Linux啟動流程

linux 詳解 啟動流程 grub mbr 內核 linux啟動流程第一部分 Linux啟動基礎知識1.1 linux centos6.8啟動流程圖 BIOS加電自檢à加載MBRà加載啟動grubà加載內核à啟動/sbin/i

Linux netstat命令,高級面試必備

bytes tool head osi ngs 進行 pen 通信 詳細信息 簡介 Netstat 命令用於顯示各種網絡相關信息,如網絡連接,路由表,接口狀態 (Interface Statistics),masquerade 連接,多播成員 (Multicast Mem

linux top 命令

ctrl+ 一次 所有 使用方法 ase 隱藏 統計 ini 前臺 top命令是Linux下常用的性能分析工具,能夠實時顯示系統中各個進程的資源占用狀況,類似於Windows的任務管理器。下面詳細介紹它的使用方法。top - 01:06:48 up 1:22, 1 user

【轉】linux awk命令

column 環境變量 最後一行 工作流程 初始 文本文件 for循環 其中 cti 簡介 awk是一個強大的文本分析工具,相對於grep的查找,sed的編輯,awk在其對數據分析並生成報告時,顯得尤為強大。簡單來說awk就是把文件逐行的讀入,以空格為默認分隔符將每行切

Linux ls命令

-c 目錄 輸出 限制 普通 排序 當前 ls -l sna ls 命令可以說是Linux下最常用的命令之一。 -a 列出目錄下的所有文件,包括以 . 開頭的隱含文件。(後有詳解)-b 把文件名中不可輸出的字符用反斜杠加字符編號(就象在c語言裏一樣)的形式列出。-c 輸出

TCP連接的狀態以及故障排查

ron 快的 watermark 概念 cer ever socket客戶端 交換 暫時 轉載自CSDN博客:http://blog.csdn.net/hguisu/article/details/38700899 TCP狀態 TCP狀態遷移路線圖 TCP連接建立三

linux lsof命令

open 日誌 文件和目錄 delete 數據報 正在 某個文件 alt targe 簡介 lsof(list open files)是一個列出當前系統打開文件的工具。在linux環境下,任何事物都以文件的形式存在,通過文件不僅僅可以訪問常規數據,還可以訪問網絡連接和硬件

linux tail 命令

基本 linux中 file tail命令 方式 sed 缺省 顯示 有效 linux ---tail命令 linux中tail命令---用於查看文件內容 最基本的是cat、more和less。 1. 如果你只想看文件的前5行,可以使用head命令,如: head -5 /