歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux進程狀態解析之R、S、D、T、Z

Linux進程狀態解析之R、S、D、T、Z

日期:2017/2/27 16:04:25   编辑:Linux教程

R (TASK_RUNNING),可執行狀態。

只有在該狀態的進程才可能在CPU上運行。同一時刻可能有多個進程處於可執行狀態,這些進程的task_struct結構(進程控制塊)被放入對應CPU的可執行隊列中(一個進程最多只能出現在一個CPU的可執行隊列中)。進程調度器從各個CPU的可執行隊列中分別選擇一個進程在該CPU上運行。
正在CPU上執行的進程定義為RUNNING狀態、可執行但尚未被調度執行的進程定義為READY狀態,這兩種狀態統一為 TASK_RUNNING狀態。

S (TASK_INTERRUPTIBLE),可中斷的睡眠狀態。

處於這個狀態的進程,因為等待某某事件的發生(比如等待socket連接、等待信號量),而被掛起。這些進程的task_struct結構被放入對應事件的等待隊列中。當這些事件發生時(由外部中斷觸發、或由其他進程觸發),對應的等待隊列中的一個或多個進程將被喚醒。
進程列表中的絕大多數進程都處於TASK_INTERRUPTIBLE狀態。CPU就這麼一兩個,進程動辄幾十上百個,如果不是絕大多數進程都在睡眠,CPU將會響應不過來。

D (TASK_UNINTERRUPTIBLE),不可中斷的睡眠狀態。

進程處於睡眠狀態,但是此刻進程是不可中斷的。不可中斷,指的並不是CPU不響應外部硬件的中斷,而是指進程不響應異步信號。絕大多數情況下,進程處在睡眠狀態時,總是應該能夠響應異步信號的。
而TASK_UNINTERRUPTIBLE狀態存在的意義在於,進程對某些硬件進行操作時(比如進程調用read系統調用對某個設備文件進行讀操作,而read系統調用最終執行到對應設備驅動的代碼,並與對應的物理設備進行交互),可能需要使用TASK_UNINTERRUPTIBLE狀態對進程進行保護,以避免進程與設備交互的過程被打斷,造成設備陷入不可控的狀態。這種情況下的TASK_UNINTERRUPTIBLE狀態總是非常短暫的,通過ps命令基本上不可能捕捉到。
linux系統中也存在容易捕捉的TASK_UNINTERRUPTIBLE狀態。執行vfork系統調用後,父進程將進入TASK_UNINTERRUPTIBLE狀態,直到子進程調用exit或exec。通過下面的代碼就能得到處於TASK_UNINTERRUPTIBLE狀態的進程:
#include <stdio.h>
#include <unistd.h>
void main()
{
if (!vfork());
sleep(100);
ruturn 0;
}

編譯運行,然後ps一下:
njs@njs:~/test$ 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狀態的父進程依然屹立不倒。

T (TASK_STOPPED or TASK_TRACED),暫停狀態或跟蹤狀態。

向進程發送一個SIGSTOP信號,它就會因響應該信號而進入TASK_STOPPED狀態(除非該進程本身處於TASK_UNINTERRUPTIBLE狀態而不響應信號)。(SIGSTOP與SIGKILL信號一樣,是非常強制的。不允許用戶進程通過signal系列的系統調用重新設置對應的信號處理函數。)向進程發送一個SIGCONT信號,可以讓其從TASK_STOPPED狀態恢復到TASK_RUNNING狀態。

Z (TASK_DEAD - EXIT_ZOMBIE),退出狀態,進程成為僵屍進程。

進程在退出的過程中,處於TASK_DEAD狀態。
在這個退出過程中,進程占有的所有資源將被回收,除了task_struct結構(以及少數資源)以外。於是進程就只剩下task_struct這麼個空殼,故稱為僵屍。之所以保留task_struct,是因為task_struct裡面保存了進程的退出碼、以及一些統計信息。而其父進程很可能會關心這些信息。釋放掉task_struct,則需要建立一些新的數據結構,以便讓父進程找到它的子進程的退出信息。
父進程可以通過wait系列的系統調用(如wait4、waitid)來等待某個或某些子進程的退出,並獲取它的退出信息。然後wait系列的系統調用會順便將子進程的屍體(task_struct)也釋放掉。子進程在退出的過程中,內核會給其父進程發送一個信號,通知父進程來“收屍”。這個信號默認是SIGCHLD,但是在通過clone系統調用創建子進程時,可以設置這個信號。
通過下面的代碼能夠制造一個EXIT_ZOMBIE狀態的進程:
#include <stdio.h>
#include <unistd.h>
void main()
{
if (fork());
while(1)
sleep(100);
}

編譯運行,然後ps一下:
njs@njs:~/test$ 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進程。它有兩項使命:1、執行系統初始化腳本,創建一系列的進程(它們都是init進程的子孫);2、在一個死循環中等待其子進程的退出事件,並調用waitid系統調用來完成“收屍”工作;init進程不會被暫停、也不會被殺死(這是由內核來保證的)。它在等待子進程退出的過程中處於TASK_INTERRUPTIBLE狀態,“收屍”過程中則處於TASK_RUNNING狀態。
Copyright © Linux教程網 All Rights Reserved