IT貓撲網(wǎng):您身邊最放心的安全下載站! 最新更新|軟件分類|軟件專題|手機(jī)版|論壇轉(zhuǎn)貼|軟件發(fā)布

您當(dāng)前所在位置: 首頁操作系統(tǒng)LINUX → linux進(jìn)程狀態(tài)淺析

linux進(jìn)程狀態(tài)淺析

時(shí)間:2015-06-28 00:00:00 來源:IT貓撲網(wǎng) 作者:網(wǎng)管聯(lián)盟 我要評(píng)論(0)

  眾所周知,現(xiàn)在的分時(shí)操作系統(tǒng)能夠在一個(gè)CPU上運(yùn)行多個(gè)程序,讓這些程序表面上看起來是在同時(shí)運(yùn)行的。linux就是這樣的一個(gè)操作系統(tǒng)。

  在linux系統(tǒng)中,每個(gè)被運(yùn)行的程序?qū)嵗龑?duì)應(yīng)一個(gè)或多個(gè)進(jìn)程。linux內(nèi)核需要對(duì)這些進(jìn)程進(jìn)行管理,以使它們?cè)谙到y(tǒng)中"同時(shí)"運(yùn)行。linux內(nèi)核對(duì)進(jìn)程的這種管理分兩個(gè)方面:進(jìn)程狀態(tài)管理,和進(jìn)程調(diào)度。本文主要介紹進(jìn)程狀態(tài)管理,進(jìn)程調(diào)度見《linux進(jìn)程調(diào)度淺析》。

  進(jìn)程狀態(tài)

  在linux下,通過ps命令我們能夠查看到系統(tǒng)中存在的進(jìn)程,以及它們的狀態(tài):

  R (TASK_RUNNING),可執(zhí)行狀態(tài)。

  只有在該狀態(tài)的進(jìn)程才可能在CPU上運(yùn)行。而同一時(shí)刻可能有多個(gè)進(jìn)程處于可執(zhí)行狀態(tài),這些進(jìn)程的task_struct結(jié)構(gòu)(進(jìn)程控制塊)被放入對(duì)應(yīng) CPU的可執(zhí)行隊(duì)列中(一個(gè)進(jìn)程最多只能出現(xiàn)在一個(gè)CPU的可執(zhí)行隊(duì)列中)。進(jìn)程調(diào)度器的任務(wù)就是從各個(gè)CPU的可執(zhí)行隊(duì)列中分別選擇一個(gè)進(jìn)程在該CPU 上運(yùn)行。

  只要可執(zhí)行隊(duì)列不為空,其對(duì)應(yīng)的CPU就不能偷懶,就要執(zhí)行其中某個(gè)進(jìn)程。一般稱此時(shí)的CPU"忙碌"。對(duì)應(yīng)的,CPU"空閑"就是指其對(duì)應(yīng)的可執(zhí)行隊(duì)列為空,以致于CPU無事可做。

  有人問,為什么死循環(huán)程序會(huì)導(dǎo)致CPU占用高呢?因?yàn)樗姥h(huán)程序基本上總是處于TASK_RUNNING狀態(tài)(進(jìn)程處于可執(zhí)行隊(duì)列中)。除非一些非常極端情況(比如系統(tǒng)內(nèi)存嚴(yán)重緊缺,導(dǎo)致進(jìn)程的某些需要使用的頁面被換出,并且在頁面需要換入時(shí)又無法分配到內(nèi)存……),否則這個(gè)進(jìn)程不會(huì)睡眠。所以CPU的可執(zhí)行隊(duì)列總是不為空(至少有這么個(gè)進(jìn)程存在),CPU也就不會(huì)"空閑"。

  很多操作系統(tǒng)教科書將正在CPU上執(zhí)行的進(jìn)程定義為RUNNING狀態(tài)、而將可執(zhí)行但是尚未被調(diào)度執(zhí)行的進(jìn)程定義為READY狀態(tài),這兩種狀態(tài)在linux下統(tǒng)一為 TASK_RUNNING狀態(tài)。

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

  處于這個(gè)狀態(tài)的進(jìn)程因?yàn)榈却衬呈录陌l(fā)生(比如等待socket連接、等待信號(hào)量),而被掛起。這些進(jìn)程的task_struct結(jié)構(gòu)被放入對(duì)應(yīng)事件的等待隊(duì)列中。當(dāng)這些事件發(fā)生時(shí)(由外部中斷觸發(fā)、或由其他進(jìn)程觸發(fā)),對(duì)應(yīng)的等待隊(duì)列中的一個(gè)或多個(gè)進(jìn)程將被喚醒。

  通過ps命令我們會(huì)看到,一般情況下,進(jìn)程列表中的絕大多數(shù)進(jìn)程都處于TASK_INTERRUPTIBLE狀態(tài)(除非機(jī)器的負(fù)載很高)。畢竟CPU就這么一兩個(gè),進(jìn)程動(dòng)輒幾十上百個(gè),如果不是絕大多數(shù)進(jìn)程都在睡眠,CPU又怎么響應(yīng)得過來。

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

  與TASK_INTERRUPTIBLE狀態(tài)類似,進(jìn)程處于睡眠狀態(tài),但是此刻進(jìn)程是不可中斷的。不可中斷,指的并不是CPU不響應(yīng)外部硬件的中斷,而是指進(jìn)程不響應(yīng)異步信號(hào)。

  絕大多數(shù)情況下,進(jìn)程處在睡眠狀態(tài)時(shí),總是應(yīng)該能夠響應(yīng)異步信號(hào)的。否則你將驚奇的發(fā)現(xiàn),kill -9竟然殺不死一個(gè)正在睡眠的進(jìn)程了!于是我們也很好理解,為什么ps命令看到的進(jìn)程幾乎不會(huì)出現(xiàn)TASK_UNINTERRUPTIBLE狀態(tài),而總是 TASK_INTERRUPTIBLE狀態(tài)。

  而TASK_UNINTERRUPTIBLE狀態(tài)存在的意義就在于,內(nèi)核的某些處理流程是不能被打斷的。如果響應(yīng)異步信號(hào),程序的執(zhí)行流程中就會(huì)被插入一段用于處理異步信號(hào)的流程(這個(gè)插入的流程可能只存在于內(nèi)核態(tài),也可能延伸到用戶態(tài)),于是原有的流程就被中斷了。(參見《linux內(nèi)核異步中斷淺析》)

  在進(jìn)程對(duì)某些硬件進(jìn)行操作時(shí)(比如進(jìn)程調(diào)用read系統(tǒng)調(diào)用對(duì)某個(gè)設(shè)備文件進(jìn)行讀操作,而read系統(tǒng)調(diào)用最終執(zhí)行到對(duì)應(yīng)設(shè)備驅(qū)動(dòng)的代碼,并與對(duì)應(yīng)的物理設(shè)備進(jìn)行交互),可能需要使用TASK_UNINTERRUPTIBLE狀態(tài)對(duì)進(jìn)程進(jìn)行保護(hù),以避免進(jìn)程與設(shè)備交互的過程被打斷,造成設(shè)備陷入不可控的狀態(tài)。這種情況下的TASK_UNINTERRUPTIBLE狀態(tài)總是非常短暫的,通過ps命令基本上不可能捕捉到。

  linux系統(tǒng)中也存在容易捕捉的TASK_UNINTERRUPTIBLE狀態(tài)。執(zhí)行vfork系統(tǒng)調(diào)用后,父進(jìn)程將進(jìn)入TASK_UNINTERRUPTIBLE狀態(tài),直到子進(jìn)程調(diào)用exit或exec(參見《神奇的vfork》)。

  通過下面的代碼就能得到處于TASK_UNINTERRUPTIBLE狀態(tài)的進(jìn)程:

  #include

  void main() {

  if (!vfork()) sleep(100);

  }

  編譯運(yùn)行,然后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

  然后我們可以試驗(yàn)一下TASK_UNINTERRUPTIBLE狀態(tài)的威力。不管kill還是kill -9,這個(gè)TASK_UNINTERRUPTIBLE狀態(tài)的父進(jìn)程依然屹立不倒。

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

  向進(jìn)程發(fā)送一個(gè)SIGSTOP信號(hào),它就會(huì)因響應(yīng)該信號(hào)而進(jìn)入TASK_STOPPED狀態(tài)(除非該進(jìn)程本身處于 TASK_UNINTERRUPTIBLE狀態(tài)而不響應(yīng)信號(hào))。(SIGSTOP與SIGKILL信號(hào)一樣,是非常強(qiáng)制的。不允許用戶進(jìn)程通過 signal系列的系統(tǒng)調(diào)用重新設(shè)置對(duì)應(yīng)的信號(hào)處理函數(shù)。)

  向進(jìn)程發(fā)送一個(gè)SIGCONT信號(hào),可以讓其從TASK_STOPPED狀態(tài)恢復(fù)到TASK_RUNNING狀態(tài)。

  當(dāng)進(jìn)程正在被跟蹤時(shí),它處于TASK_TRACED這個(gè)特殊的狀態(tài)。"正在被跟蹤"指的是進(jìn)程暫停下來,等待跟蹤它的進(jìn)程對(duì)它進(jìn)行操作。比如在gdb中對(duì)被跟蹤的進(jìn)程下一個(gè)斷點(diǎn),進(jìn)程在斷點(diǎn)處停下來的時(shí)候就處于TASK_TRACED狀態(tài)。而在其他時(shí)候,被跟蹤的進(jìn)程還是處于前面提到的那些狀態(tài)。

#p#副標(biāo)題#e#

  對(duì)于進(jìn)程本身來說,TASK_STOPPED和TASK_TRACED狀態(tài)很類似,都是表示進(jìn)程暫停下來。

  而TASK_TRACED狀態(tài)相當(dāng)于在TASK_STOPPED之上多了一層保護(hù),處于TASK_TRACED狀態(tài)的進(jìn)程不能響應(yīng)SIGCONT信號(hào)而被喚醒。只能等到調(diào)試進(jìn)程通過ptrace系統(tǒng)調(diào)用執(zhí)行PTRACE_CONT、PTRACE_DETACH等操作(通過ptrace系統(tǒng)調(diào)用的參數(shù)指定操作),或調(diào)試進(jìn)程退出,被調(diào)試的進(jìn)程才能恢復(fù)TASK_RUNNING狀態(tài)。

  Z (TASK_DEAD - EXIT_ZOMBIE),退出狀態(tài),進(jìn)程成為僵尸進(jìn)程。

  進(jìn)程在退出的過程中,處于TASK_DEAD狀態(tài)。

  在這個(gè)退出過程中,進(jìn)程占有的所有資源將被回收,除了task_struct結(jié)構(gòu)(以及少數(shù)資源)以外。于是進(jìn)程就只剩下task_struct這么個(gè)空殼,故稱為僵尸。

  之所以保留task_struct,是因?yàn)閠ask_struct里面保存了進(jìn)程的退出碼、以及一些統(tǒng)計(jì)信息。而其父進(jìn)程很可能會(huì)關(guān)心這些信息。比如在shell中,$?變量就保存了最后一個(gè)退出的前臺(tái)進(jìn)程的退出碼,而這個(gè)退出碼往往被作為if語句的判斷條件。

  當(dāng)然,內(nèi)核也可以將這些信息保存在別的地方,而將task_struct結(jié)構(gòu)釋放掉,以節(jié)省一些空間。但是使用task_struct結(jié)構(gòu)更為方便,因?yàn)樵趦?nèi)核中已經(jīng)建立了從pid到task_struct查找關(guān)系,還有進(jìn)程間的父子關(guān)系。釋放掉task_struct,則需要建立一些新的數(shù)據(jù)結(jié)構(gòu),以便讓父進(jìn)程找到它的子進(jìn)程的退出信息。

  父進(jìn)程可以通過wait系列的系統(tǒng)調(diào)用(如wait4、waitid)來等待某個(gè)或某些子進(jìn)程的退出,并獲取它的退出信息。然后wait系列的系統(tǒng)調(diào)用會(huì)順便將子進(jìn)程的尸體(task_struct)也釋放掉。

  子進(jìn)程在退出的過程中,內(nèi)核會(huì)給其父進(jìn)程發(fā)送一個(gè)信號(hào),通知父進(jìn)程來"收尸"。這個(gè)信號(hào)默認(rèn)是SIGCHLD,但是在通過clone系統(tǒng)調(diào)用創(chuàng)建子進(jìn)程時(shí),可以設(shè)置這個(gè)信號(hào)。

  通過下面的代碼能夠制造一個(gè)EXIT_ZOMBIE狀態(tài)的進(jìn)程:

  #include

  void main() {

  if (fork())

  while(1) sleep(100);

  }

  編譯運(yùn)行,然后ps一下:

  kouu@kouu-one:~/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

  只要父進(jìn)程不退出,這個(gè)僵尸狀態(tài)的子進(jìn)程就一直存在。那么如果父進(jìn)程退出了呢,誰又來給子進(jìn)程"收尸"?

  當(dāng)進(jìn)程退出的時(shí)候,會(huì)將它的所有子進(jìn)程都托管給別的進(jìn)程(使之成為別的進(jìn)程的子進(jìn)程)。托管給誰呢?可能是退出進(jìn)程所在進(jìn)程組的下一個(gè)進(jìn)程(如果存在的話),或者是1號(hào)進(jìn)程。所以每個(gè)進(jìn)程、每時(shí)每刻都有父進(jìn)程存在。除非它是1號(hào)進(jìn)程。

  1號(hào)進(jìn)程,pid為1的進(jìn)程,又稱init進(jìn)程。

  linux系統(tǒng)啟動(dòng)后,第一個(gè)被創(chuàng)建的用戶態(tài)進(jìn)程就是init進(jìn)程。它有兩項(xiàng)使命:

  1、執(zhí)行系統(tǒng)初始化腳本,創(chuàng)建一系列的進(jìn)程(它們都是init進(jìn)程的子孫);

  2、在一個(gè)死循環(huán)中等待其子進(jìn)程的退出事件,并調(diào)用waitid系統(tǒng)調(diào)用來完成"收尸"工作;

  init進(jìn)程不會(huì)被暫停、也不會(huì)被殺死(這是由內(nèi)核來保證的)。它在等待子進(jìn)程退出的過程中處于TASK_INTERRUPTIBLE狀態(tài),"收尸"過程中則處于TASK_RUNNING狀態(tài)。

  X (TASK_DEAD - EXIT_DEAD),退出狀態(tài),進(jìn)程即將被銷毀。

  而進(jìn)程在退出過程中也可能不會(huì)保留它的task_struct。比如這個(gè)進(jìn)程是多線程程序中被detach過的進(jìn)程(進(jìn)程?線程?參見《linux線程淺析》)?;蛘吒高M(jìn)程通過設(shè)置SIGCHLD信號(hào)的handler為SIG_IGN,顯式的忽略了SIGCHLD信號(hào)。(這是posix的規(guī)定,盡管子進(jìn)程的退出信號(hào)可以被設(shè)置為SIGCHLD以外的其他信號(hào)。)

  此時(shí),進(jìn)程將被置于EXIT_DEAD

關(guān)鍵詞標(biāo)簽:linux進(jìn)程狀態(tài)

相關(guān)閱讀

文章評(píng)論
發(fā)表評(píng)論

熱門文章 安裝紅帽子RedHat Linux9.0操作系統(tǒng)教程 安裝紅帽子RedHat Linux9.0操作系統(tǒng)教程 Tomcat9.0如何安裝_Tomcat9.0環(huán)境變量配置方法 Tomcat9.0如何安裝_Tomcat9.0環(huán)境變量配置方法 多種操作系統(tǒng)NTP客戶端配置 多種操作系統(tǒng)NTP客戶端配置 Linux操作系統(tǒng)修改IP Linux操作系統(tǒng)修改IP

相關(guān)下載

    人氣排行 Linux下獲取CPUID、硬盤序列號(hào)與MAC地址 dmidecode命令查看內(nèi)存型號(hào) linux tc實(shí)現(xiàn)ip流量限制 安裝紅帽子RedHat Linux9.0操作系統(tǒng)教程 linux下解壓rar文件 lcx.exe、nc.exe、sc.exe入侵中的使用方法 Ubuntu linux 關(guān)機(jī)、重啟、注銷 命令 查看linux服務(wù)器硬盤IO讀寫負(fù)載