Linux進程狀態(tài)解析之R、S、D
Linux是一個多用戶,多任務(wù)的系統(tǒng),可以同時運行多個用戶的多個程序,就必然會產(chǎn)生很多的進程,而每個進程會有不同的狀態(tài)。
Linux進程狀態(tài):R (TASK_RUNNING),可執(zhí)行狀態(tài)。
只有在該狀態(tài)的進程才可能在CPU上運行。而同一時刻可能有多個進程處于可執(zhí)行狀態(tài),這些進程的task_struct結(jié)構(gòu)(進程控制塊)被放入對應(yīng)CPU的可執(zhí)行隊列中(一個進程最多只能出現(xiàn)在一個CPU的可執(zhí)行隊列中)。進程調(diào)度器的任務(wù)就是從各個CPU的可執(zhí)行隊列中分別選擇一個進程在該CPU上運行。
很多操作系統(tǒng)教科書將正在CPU上執(zhí)行的進程定義為RUNNING狀態(tài)、而將可執(zhí)行但是尚未被調(diào)度執(zhí)行的進程定義為READY狀態(tài),這兩種狀態(tài)在linux下統(tǒng)一為 TASK_RUNNING狀態(tài)。
Linux進程狀態(tài):S (TASK_INTERRUPTIBLE),可中斷的睡眠狀態(tài)。
處于這個狀態(tài)的進程因為等待某某事件的發(fā)生(比如等待socket連接、等待信號量),而被掛起。這些進程的task_struct結(jié)構(gòu)被放入對應(yīng)事件的等待隊列中。當(dāng)這些事件發(fā)生時(由外部中斷觸發(fā)、或由其他進程觸發(fā)),對應(yīng)的等待隊列中的一個或多個進程將被喚醒。
通過ps命令我們會看到,一般情況下,進程列表中的絕大多數(shù)進程都處于TASK_INTERRUPTIBLE狀態(tài)(除非機器的負(fù)載很高)。畢竟CPU就這么一兩個,進程動輒幾十上百個,如果不是絕大多數(shù)進程都在睡眠,CPU又怎么響應(yīng)得過來。
Linux進程狀態(tài):D (TASK_UNINTERRUPTIBLE),不可中斷的睡眠狀態(tài)。
與TASK_INTERRUPTIBLE狀態(tài)類似,進程處于睡眠狀態(tài),但是此刻進程是不可中斷的。不可中斷,指的并不是CPU不響應(yīng)外部硬件的中斷,而是指進程不響應(yīng)異步信號。
絕大多數(shù)情況下,進程處在睡眠狀態(tài)時,總是應(yīng)該能夠響應(yīng)異步信號的。否則你將驚奇的發(fā)現(xiàn),kill -9竟然殺不死一個正在睡眠的進程了!于是我們也很好理解,為什么ps命令看到的進程幾乎不會出現(xiàn)TASK_UNINTERRUPTIBLE狀態(tài),而總是TASK_INTERRUPTIBLE狀態(tài)。
而TASK_UNINTERRUPTIBLE狀態(tài)存在的意義就在于,內(nèi)核的某些處理流程是不能被打斷的。如果響應(yīng)異步信號,程序的執(zhí)行流程中就會被插入一段用于處理異步信號的流程(這個插入的流程可能只存在于內(nèi)核態(tài),也可能延伸到用戶態(tài)),于是原有的流程就被中斷了。(參見《linux內(nèi)核異步中斷淺析》)
在進程對某些硬件進行操作時(比如進程調(diào)用read系統(tǒng)調(diào)用對某個設(shè)備文件進行讀操作,而read系統(tǒng)調(diào)用最終執(zhí)行到對應(yīng)設(shè)備驅(qū)動的代碼,并與對應(yīng)的物理設(shè)備進行交互),可能需要使用TASK_UNINTERRUPTIBLE狀態(tài)對進程進行保護,以避免進程與設(shè)備交互的過程被打斷,造成設(shè)備陷入不可控的狀態(tài)。這種情況下的TASK_UNINTERRUPTIBLE狀態(tài)總是非常短暫的,通過ps命令基本上不可能捕捉到。
linux系統(tǒng)中也存在容易捕捉的TASK_UNINTERRUPTIBLE狀態(tài)。執(zhí)行vfork系統(tǒng)調(diào)用后,父進程將進入TASK_UNINTERRUPTIBLE狀態(tài),直到子進程調(diào)用exit或exec(參見《神奇的vfork》)。
通過下面的代碼就能得到處于TASK_UNINTERRUPTIBLE狀態(tài)的進程:
- #include
- void main() {
- if (!vfork()) sleep(100);
- }
編譯運行,然后ps一下:
- kouu@kouu-one:~/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狀態(tài)的威力。不管kill還是kill -9,這個TASK_UNINTERRUPTIBLE狀態(tài)的父進程依然屹立不倒。
【編輯推薦】