Linux 進程狀態

來源:互聯網
上載者:User

Linux是一個多使用者,多任務的系統,可以同時運行多個使用者的多個程式,就必然會產生很多的進程,而每個進程會有不同的狀態。 在下文將對進程的

R、S、D、T、Z、X 六種狀態做個說明。

PROCESS STATE CODES

Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe the state of a process.

D Uninterruptible sleep (usually IO)

R Running or runnable (on run queue)

S Interruptible sleep (waiting for an event to complete)

T Stopped, either by a job control signal or because it is being traced.

W paging (not valid since the 2.6.xx kernel)

X dead (should never be seen)

Z Defunct ("zombie") process, terminated but not

reaped by its parent.

For BSD formats and when the stat keyword is used,additional characters may be displayed:

< high-priority (not nice to other users)

N low-priority (nice to other users)

L has pages locked into memory (for real-time and custom IO)

s is a session leader

l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)

+ is in the foreground process group

一. 查看進程的狀態

1.1 使用PS命令

[root@localhost]# ps -a -o pid,ppid,stat,command -u oracle

PID PPID STAT COMMAND

637 1 Ss oracleXEZF (LOCAL=NO)

729 1 Ss oracleXEZF (LOCAL=NO)

1144 1103 S+ top

1230 1 Ss oracleXEZF (LOCAL=NO)

1289 1145 S+ vmstat 10

1699 1 Ss oracleXEZF (LOCAL=NO)

1827 1294 R+ ps -a -o pid,ppid,stat,command -u oracle

3410 1 Ss ora_pmon_XEZF

3412 1 Ss ora_psp0_XEZF

3414 1 Ss ora_mman_XEZF

3416 1 Ss ora_dbw0_XEZF

3418 1 Ss ora_lgwr_XEZF

3420 1 Ss ora_ckpt_XEZF

3422 1 Ss ora_smon_XEZF

3424 1 Ss ora_reco_XEZF

3426 1 Ss ora_mmon_XEZF

3428 1 Ss ora_mmnl_XEZF

3430 1 Ss ora_d000_XEZF

3432 1 Ss ora_d001_XEZF

3434 1 Ss ora_s000_XEZF

3436 1 Ss ora_s001_XEZF

3438 1 Ss ora_s002_XEZF

3488 1 Ssl /home/oracle_app/bin/tnslsnr LISTENER -inherit

11167 1 Ss oracleXEZF (LOCAL=NO)

11423 1 Ss oracleXEZF (LOCAL=NO)

11425 1 Ss oracleXEZF (LOCAL=NO)

11429 1 Ss oracleXEZF (LOCAL=NO)

14867 1 Ss oracleXEZF (LOCAL=NO)

19323 1 Ss oracleXEZF (LOCAL=NO)

用ps 的 – l 選項,得到更詳細的進程資訊:

(1)F(Flag):一系列數位和,表示進程的目前狀態。這些數位含義為:

00:若單獨顯示,表示此進程已被終止。

01:進程是核心進程的一部分,常駐於系統主存。如:sched,vhand,bdflush。

02:Parent is tracing process.

04 :Tracing parent's signal has stopped the process; the parent is waiting ( ptrace(S)).

10:進程在優先順序低於或等於25時,進入休眠狀態,而且不能用訊號喚醒,例如在等待一個inode被建立時。

20:進程被裝入主存(primary memory)

40:進程被鎖在主存,在事務完成前不能被置換。

(2) 進程狀態:S(state)

O:進程正在處理器運行,這個狀態從來木見過.

S:休眠狀態(sleeping)

R:等待運行(runable)R Running or runnable (on run queue) 進程處於運行或就緒狀態

I:空閑狀態(idle)

Z:殭屍狀態(zombie)

T:跟蹤狀態(Traced)

B:進程正在等待更多的記憶體頁

D:不可中斷的深度睡眠,一般由IO引起,同步IO在做讀或寫操作時,cpu不能做其它事情,只能等待,這時進程處於這種狀態,如果程式採用非同步IO,這種狀態應該就很少見到了

(3)C(cpu usage):cpu利用率的估算值

1.2 使用Top命令中的S 欄位

pid user pr ni virt res shr s %cpu %mem time+ command

11423 oracle 16 0 627m 170m 168m R 32 9.0 4110:21 oracle

3416 oracle 15 0 650m 158m 138m S 0 8.4 0:07.12 oracle

11167 oracle 15 0 626m 151m 149m S 0 8.0 400:20.77 oracle

11429 oracle 15 0 626m 148m 147m S 0 7.9 812:05.71 oracle

3422 oracle 18 0 627m 140m 137m S 0 7.4 1:12.23 oracle

1230 oracle 15 0 639m 107m 96m S 0 5.7 0:10.00 oracle

637 oracle 15 0 629m 76m 73m S 0 4.0 0:04.31 oracle

二. 進程狀態說明

2.1 R (task_running) : 可執行狀態

只有在該狀態的進程才可能在CPU上運行。而同一時刻可能有多個進程處於可執行狀態,這些進程的task_struct結構(進程式控制制塊)被放入對應CPU的可執行隊列中(一個進程最多隻能出現在一個CPU的可執行隊列中)。進程調度器的任務就是從各個CPU的可執行隊列中分別選擇一個進程在該CPU上運行。

很多作業系統教科書將正在CPU上執行的進程定義為RUNNING狀態、而將可執行但是尚未被調度執行的進程定義為READY狀態,這兩種狀態在linux下統一為 TASK_RUNNING狀態。

2.2 S (task_interruptible): 可中斷的睡眠狀態

處於這個狀態的進程因為等待某某事件的發生(比如等待socket串連、等待訊號量),而被掛起。這些進程的task_struct結構被放入對應事件的等待隊列中。當這些事件發生時(由外部中斷觸發、或由其他進程觸發),對應的等待隊列中的一個或多個進程將被喚醒。

通過ps命令我們會看到,一般情況下,進程列表中的絕大多數進程都處於task_interruptible狀態(除非機器的負載很高)。畢竟CPU就這麼一兩個,進程動輒幾十上百個,如果不是絕大多數進程都在睡眠,CPU又怎麼響應得過來。

2.3 D (task_uninterruptible): 不可中斷的睡眠狀態

與task_interruptible狀態類似,進程處於睡眠狀態,但是此刻進程是不可中斷的。不可中斷,指的並不是CPU不響應外部硬體的中斷,而是指進程不響應非同步訊號。
絕大多數情況下,進程處在睡眠狀態時,總是應該能夠響應非同步訊號的。但是uninterruptible sleep 狀態的進程不接受外來的任何訊號,因此無法用kill殺掉這些處於D狀態的進程,無論是”kill”, “kill -9″還是”kill -15″,這種情況下,一個可選的方法就是reboot。

處於uninterruptible sleep狀態的進程通常是在等待IO,比如磁碟IO,網路IO,其他外設IO,如果進程正在等待的IO在較長的時間內都沒有響應,那麼就被ps看到了,同時也就意味著很有可能有IO出了問題,可能是外設本身出了故障,也可能是比如掛載的遠程檔案系統已經不可訪問了.

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

在進程對某些硬體進行操作時(比如進程調用read系統調用對某個裝置檔案進行讀操作,而read系統調用最終執行到對應裝置驅動的代碼,並與對應的物理裝置進行互動),可能需要使用task_uninterruptible狀態對進程進行保護,以避免進程與裝置互動的過程被打斷,造成裝置陷入不可控的狀態。這種情況下的task_uninterruptible狀態總是非常短暫的,通過ps命令基本上不可能捕捉到。

我們通過vmstat 命令中procs下的b 可以來查看是否有處於uninterruptible 狀態的進程。 該命令只能顯示數量。

In computer operating systems terminology, a sleeping process can either be interruptible (woken via signals) or uninterruptible (woken explicitly). An uninterruptible sleep state is a sleep state that cannot handle a signal (such as waiting for disk or network IO (input/output)).

When the process is sleeping uninterruptibly, the signal will be noticed when the process returns from the system call or trap.

-- 這句是關鍵。 當處於uninterruptibly sleep 狀態時,只有當進程從system 調用返回時,才通知signal。

A process which ends up in “D” state for any measurable length of time is trapped in the midst of a system call (usually an I/O operation on a device — thus the initial in the ps output).

Such a process cannot be killed — it would risk leaving the kernel in an inconsistent state, leading to a panic. In general you can consider this to be a bug in the device driver that the process is accessing.

2.4 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狀態。

2.5 Z (task_dead - exit_zombie):退出狀態,進程成為殭屍進程

在Linux進程的狀態中,殭屍進程是非常特殊的一種,它是已經結束了的進程,但是沒有從進程表中刪除。太多了會導致進程表裡麵條目滿了,進而導致系統崩潰,倒是不佔用其他系統資源。

它已經放棄了幾乎所有記憶體空間,沒有任何可執行代碼,也不能被調度,僅僅在進程列表中保留一個位置,記載該進程的退出狀態等資訊供其他進程收集,除此之外,殭屍進程不再佔有任何記憶體空間。

進程在退出的過程中,處於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系統調用建立子進程時,可以設定這個訊號。

如果他的父進程沒安裝SIGCHLD訊號處理函數調用wait或waitpid()等待子進程結束,又沒有顯式忽略該訊號,那麼它就一直保持殭屍狀態,子進程的屍體(task_struct)也就無法釋放掉。

如果這時父進程結束了,那麼init進程自動會接手這個子進程,為它收屍,它還是能被清除的。但是如果如果父進程是一個迴圈,不會結束,那麼子進程就會一直保持殭屍狀態,這就是為什麼系統中有時會有很多的殭屍進程。

當進程退出的時候,會將它的所有子進程都託管給別的進程(使之成為別的進程的子進程)。託管的進程可能是退出進程所在進程組的下一個進程(如果存在的話),或者是1號進程。所以每個進程、每時每刻都有父進程存在。除非它是1號進程。1號進程,pid為1的進程,又稱init進程。

linux系統啟動後,第一個被建立的使用者態進程就是init進程。它有兩項使命:
1、執行系統初始化指令碼,建立一系列的進程(它們都是init進程的子孫);
2、在一個死迴圈中等待其子進程的退出事件,並調用waitid系統調用來完成“收屍”工作;

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

Unix/Linux 處理殭屍進程的方法:

找出父進程號,然後kill 父進程,之後子進程(殭屍進程)會被託管到其他進程,如init進程,然後由init進程將子進程的屍體(task_struct)釋放掉。

除了通過ps 的狀態來查看Zombi進程,還可以用如下命令查看:

[oracle@rac1 ~]$ ps -ef|grep defun

oracle 13526 12825 0 16:48 pts/1 00:00:00 grep defun

oracle 28330 28275 0 May18 ? 00:00:00 [Xsession] <defunct>

殭屍進程解決辦法:

(1)改寫父進程,在子進程死後要為它收屍。

具體做法是接管SIGCHLD訊號。子進程死後,會發送SIGCHLD訊號給父進程,父進程收到此訊號後,執行 waitpid()函數為子進程收屍。這是基於這樣的原理:就算父進程沒有調用wait,核心也會向它發送SIGCHLD訊息,儘管對的預設處理是忽略,如果想響應這個訊息,可以設定一個處理函數。

(2)把父進程殺掉。

父進程死後,殭屍進程成為"孤兒進程",過繼給1號進程init,init始終會負責清理殭屍進程.它產生的所有殭屍進程也跟著消失。如:

kill -9 `ps -ef | grep "Process Name" | awk '{ print $3 }'`
其中,“Process Name”為處於zombie狀態的進程名。

(3)殺父進程不行的話,就嘗試用skill -t TTY關閉相應終端,TTY是進程相應的tty號(終端號)。但是,ps可能會查不到特定進程的tty號,這時就需要自己判斷了。
(4)重啟系統,這也是最常用到方法之一。

2.6 X (task_dead - exit_dead):退出狀態,進程即將被銷毀

進程在退出過程中也可能不會保留它的task_struct。比如這個進程是多線程程式中被detach過的進程。或者父進程通過設定sigchld訊號的handler為sig_ign,顯式的忽略了sigchld訊號。(這是posix的規定,儘管子進程的退出訊號可以被設定為sigchld以外的其他訊號。)
此時,進程將被置於exit_dead退出狀態,這意味著接下來的代碼立即就會將該進程徹底釋放。所以exit_dead狀態是非常短暫的,幾乎不可能通過ps命令捕捉到。

三. 進程狀態變化說明

3.1 進程的初始狀態

進程是通過fork系列的系統調用(fork、clone、vfork)來建立的,核心(或核心模組)也可以通過kernel_thread函數建立核心進程。這些建立子進程的函數本質上都完成了相同的功能——將調用進程複製一份,得到子進程。(可以通過選項參數來決定各種資源是共用、還是私人。)
那麼既然調用進程處於task_running狀態(否則,它若不是正在運行,又怎麼進行調用?),則子進程預設也處於task_running狀態。
另外,在系統調用調用clone和核心功能kernel_thread也接受clone_stopped選項,從而將子進程的初始狀態置為task_stopped。

3.2 進程狀態變遷

進程自建立以後,狀態可能發生一系列的變化,直到進程退出。而儘管進程狀態有好幾種,但是進程狀態的變遷卻只有兩個方向——從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系統調用)。
顯然,這兩種情況都只能發生在進程正在cpu上執行的情況下。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.