linux 程式 狀態
linux程式狀態
眾所周知,現在的分時作業系統能夠在一個CPU上執行多個程式,讓這些程式表面上看起來是在同時執行的。linux就是這樣的一個作業系統。
在linux系統中,每個被執行的程式例項對應一個或多個程式。linux核心需要對這些程式進行管理,以使它們在系統中“同時”執行。linux核心對程式的這種管理分兩個方面:程式狀態管理,和程式排程。本文主要介紹程式狀態管理,程式排程見《linux程式排程淺析》。
程式狀態
在linux下,通過ps命令我們能夠檢視到系統中存在的程式,以及它們的狀態:
R (TASK_RUNNING),可執行狀態。
只有在該狀態的程式才可能在CPU上執行。而同一時刻可能有多個程式處於可執行狀態,這些程式的task_struct結構(程式控制塊)被放入對應 CPU的可執行佇列中(一個程式最多隻能出現在一個CPU的可執行佇列中)。程式排程器的任務就是從各個CPU的可執行佇列中分別選擇一個程式在該CPU 上執行。
只要可執行佇列不為空,其對應的CPU就不能偷懶,就要執行其中某個程式。一般稱此時的CPU“忙碌”。對應的,CPU“空閒”就是指其對應的可執行佇列為空,以致於CPU無事可做。
有人問,為什麼死迴圈程式會導致CPU佔用高呢?因為死迴圈程式基本上總是處於TASK_RUNNING狀態(程式處於可執行佇列中)。除非一些非常極端情況(比如系統記憶體嚴重緊缺,導致程式的某些需要使用的頁面被換出,並且在頁面需要換入時又無法分配到記憶體……),否則這個程式不會睡眠。所以 CPU的可執行佇列總是不為空(至少有這麼個程式存在),CPU也就不會“空閒”。
很多作業系統教科書將正在CPU上執行的程式定義為RUNNING狀態、而將可執行但是尚未被排程執行的程式定義為READY狀態,這兩種狀態在linux下統一為 TASK_RUNNING狀態。
S (TASK_INTERRUPTIBLE),可中斷的睡眠狀態。
處於這個狀態的程式因為等待某某事件的發生(比如等待socket連線、等待訊號量),而被掛起。這些程式的task_struct結構被放入對應事件的等待佇列中。當這些事件發生時(由外部中斷觸發、或由其他程式觸發),對應的等待佇列中的一個或多個程式將被喚醒。
通過ps命令我們會看到,一般情況下,程式列表中的絕大多數程式都處於TASK_INTERRUPTIBLE狀態(除非機器的負載很高)。畢竟CPU就這麼一兩個,程式動輒幾十上百個,如果不是絕大多數程式都在睡眠,CPU又怎麼響應得過來。
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狀態的程式:
#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狀態的威力。不管kill還是kill -9,這個TASK_UNINTERRUPTIBLE狀態的父程式依然屹立不倒。
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狀態。
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狀態的程式:
#include
void main() {
if (fork())
while(1) sleep(100);
}
編譯執行,然後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
只要父程式不退出,這個殭屍狀態的子程式就一直存在。那麼如果父程式退出了呢,誰又來給子程式“收屍”?
當程式退出的時候,會將它的所有子程式都託管給別的程式(使之成為別的程式的子程式)。託管給誰呢?可能是退出程式所在程式組的下一個程式(如果存在的話),或者是1號程式。所以每個程式、每時每刻都有父程式存在。除非它是1號程式。
1號程式,pid為1的程式,又稱init程式。
linux系統啟動後,第一個被建立的使用者態程式就是init程式。它有兩項使命:
1、執行系統初始化指令碼,建立一系列的程式(它們都是init程式的子孫);
2、在一個死迴圈中等待其子程式的退出事件,並呼叫waitid系統呼叫來完成“收屍”工作;
init程式不會被暫停、也不會被殺死(這是由核心來保證的)。它在等待子程式退出的過程中處於TASK_INTERRUPTIBLE狀態,“收屍”過程中則處於TASK_RUNNING狀態。
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系統呼叫)。
顯然,這兩種情況都只能發生在程式正在CPU上執行的情況下。
眾所周知,現在的分時作業系統能夠在一個CPU上執行多個程式,讓這些程式表面上看起來是在同時執行的。linux就是這樣的一個作業系統。
在linux系統中,每個被執行的程式例項對應一個或多個程式。linux核心需要對這些程式進行管理,以使它們在系統中“同時”執行。linux核心對程式的這種管理分兩個方面:程式狀態管理,和程式排程。本文主要介紹程式狀態管理,程式排程見《linux程式排程淺析》。
程式狀態
在linux下,通過ps命令我們能夠檢視到系統中存在的程式,以及它們的狀態:
R (TASK_RUNNING),可執行狀態。
只有在該狀態的程式才可能在CPU上執行。而同一時刻可能有多個程式處於可執行狀態,這些程式的task_struct結構(程式控制塊)被放入對應 CPU的可執行佇列中(一個程式最多隻能出現在一個CPU的可執行佇列中)。程式排程器的任務就是從各個CPU的可執行佇列中分別選擇一個程式在該CPU 上執行。
只要可執行佇列不為空,其對應的CPU就不能偷懶,就要執行其中某個程式。一般稱此時的CPU“忙碌”。對應的,CPU“空閒”就是指其對應的可執行佇列為空,以致於CPU無事可做。
有人問,為什麼死迴圈程式會導致CPU佔用高呢?因為死迴圈程式基本上總是處於TASK_RUNNING狀態(程式處於可執行佇列中)。除非一些非常極端情況(比如系統記憶體嚴重緊缺,導致程式的某些需要使用的頁面被換出,並且在頁面需要換入時又無法分配到記憶體……),否則這個程式不會睡眠。所以 CPU的可執行佇列總是不為空(至少有這麼個程式存在),CPU也就不會“空閒”。
很多作業系統教科書將正在CPU上執行的程式定義為RUNNING狀態、而將可執行但是尚未被排程執行的程式定義為READY狀態,這兩種狀態在linux下統一為 TASK_RUNNING狀態。
S (TASK_INTERRUPTIBLE),可中斷的睡眠狀態。
處於這個狀態的程式因為等待某某事件的發生(比如等待socket連線、等待訊號量),而被掛起。這些程式的task_struct結構被放入對應事件的等待佇列中。當這些事件發生時(由外部中斷觸發、或由其他程式觸發),對應的等待佇列中的一個或多個程式將被喚醒。
通過ps命令我們會看到,一般情況下,程式列表中的絕大多數程式都處於TASK_INTERRUPTIBLE狀態(除非機器的負載很高)。畢竟CPU就這麼一兩個,程式動輒幾十上百個,如果不是絕大多數程式都在睡眠,CPU又怎麼響應得過來。
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狀態的程式:
#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狀態的威力。不管kill還是kill -9,這個TASK_UNINTERRUPTIBLE狀態的父程式依然屹立不倒。
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狀態。
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狀態的程式:
#include
void main() {
if (fork())
while(1) sleep(100);
}
編譯執行,然後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
只要父程式不退出,這個殭屍狀態的子程式就一直存在。那麼如果父程式退出了呢,誰又來給子程式“收屍”?
當程式退出的時候,會將它的所有子程式都託管給別的程式(使之成為別的程式的子程式)。託管給誰呢?可能是退出程式所在程式組的下一個程式(如果存在的話),或者是1號程式。所以每個程式、每時每刻都有父程式存在。除非它是1號程式。
1號程式,pid為1的程式,又稱init程式。
linux系統啟動後,第一個被建立的使用者態程式就是init程式。它有兩項使命:
1、執行系統初始化指令碼,建立一系列的程式(它們都是init程式的子孫);
2、在一個死迴圈中等待其子程式的退出事件,並呼叫waitid系統呼叫來完成“收屍”工作;
init程式不會被暫停、也不會被殺死(這是由核心來保證的)。它在等待子程式退出的過程中處於TASK_INTERRUPTIBLE狀態,“收屍”過程中則處於TASK_RUNNING狀態。
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系統呼叫)。
顯然,這兩種情況都只能發生在程式正在CPU上執行的情況下。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25897606/viewspace-713720/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Linux 程式狀態淺析Linux
- linux程式狀態詳解Linux
- Linux程式狀態——top,ps中看到程式狀態D,S的含義Linux
- linux動態檢視某組程式狀態的辦法Linux
- Linux Shell程式設計(11)——退出和退出狀態Linux程式設計
- Linux下用netstat檢視網路狀態、埠狀態Linux
- LINUX netstat連線狀態解析及TCP狀態轉換LinuxTCP
- 程式的3種狀態
- 程式的建立和程式的狀態
- linux檢查埠狀態命令Linux
- Linux如何檢視系統和程式的執行狀態?Linux
- 小程式全域性狀態管理
- 程式的狀態與轉換
- shell程式的結束狀態
- Linux iostat監測IO狀態LinuxiOS
- linux perl 檢視檔案狀態Linux
- 在Linux中,程序狀態有哪些?Linux
- HTTP狀態程式碼是什麼?HTTP
- Linux系統R、S、D、T、Z集中程式狀態的解析Linux
- Linux作業系統中如何檢視當前程式的狀態?Linux作業系統
- 【架構設計】無狀態狀態機在程式碼中的實踐架構
- Linux中系統狀態檢測命令Linux
- Linux下共享VG改變活動狀態Linux
- Linux 檢視網路連線狀態Linux
- Linux-iostat監測IO狀態LinuxiOS
- 前端狀態管理與有限狀態機前端
- ASP.NET Core 應用程式狀態ASP.NET
- 利用btrace線上監控java程式狀態Java
- 狀態模式替代箭頭型程式碼模式
- HTTP 1.1狀態程式碼及其含義HTTP
- 怎麼監控 Linux 伺服器狀態?Linux伺服器
- kali linux如何開啟電源狀態通知Linux
- 初識程式(一)——作業系統,程式管理,程式狀態作業系統
- React 狀態管理:狀態與生命週期React
- 有狀態和無狀態的區別
- 狀態模式模式
- 狀態機
- 狀態碼