Linux 系统应用编程——进程基础
一、Linux下多任務(wù)機制的介紹
???????? Linux有一特性是多任務(wù),多任務(wù)處理是指用戶可以在同一時間內(nèi)運行多個應(yīng)用程序,每個正在執(zhí)行的應(yīng)用程序被稱為一個任務(wù)。
?????????多任務(wù)操作系統(tǒng)使用某種調(diào)度(shedule)策略(由內(nèi)核來執(zhí)行)支持多個任務(wù)并發(fā)執(zhí)行。事實上,(單核)處理器在某一時刻只能執(zhí)行一個任務(wù)。每個任務(wù)創(chuàng)建時被分配時間片(幾十到上百毫秒),任務(wù)執(zhí)行(占用CPU)時,時間片遞減。操作系統(tǒng)會在當(dāng)前任務(wù)的時間片用完時調(diào)度執(zhí)行其他任務(wù)。由于任務(wù)會頻繁地切換執(zhí)行,因此給用戶多個任務(wù)運行的感覺。所以可以說,多任務(wù)由“時間片 + 輪換”來實現(xiàn)。多任務(wù)操作系統(tǒng)通常有三個基本概念:任務(wù)、進程和線程,現(xiàn)在,我們先學(xué)習(xí)進程:
?
進程的基本概念
????????進程是指一個具有獨立功能的程序在某個數(shù)據(jù)集合上的動態(tài)執(zhí)行過程,它是操作系統(tǒng)進行資源分配和調(diào)度的基本單元。簡單的說,進程是一個程序的一次執(zhí)行的過程。
??????? 進程具有并發(fā)性、動態(tài)性、交互性和獨立性等主要特性。
????????進程和程序是有本質(zhì)區(qū)別的:
1)程序( program )是一段靜態(tài)的代碼,是保存在非易失性存儲器(磁盤)上的指令和數(shù)據(jù)的有序集合,沒有任何執(zhí)行的概念;
2)進程( process )是一個動態(tài)的概念,它是程序的一次執(zhí)行過程(在RAM上執(zhí)行),包括了動態(tài)創(chuàng)建、調(diào)度、執(zhí)行和消亡的整個過程,它是程序執(zhí)行和資源管理的最小單位。
這里,我們可以看到,進程由兩部分組成:內(nèi)存地址空間 + task_struct ,task_struct 下面我們會講到,內(nèi)存地址空間就是我們程序在內(nèi)存中運行(進程)時所開辟的4GB虛擬地址空間,用于存放代碼段、數(shù)據(jù)段、堆、棧等;
從操作系統(tǒng)的角度看,進程是程序執(zhí)行時相關(guān)資源的總稱。當(dāng)進程結(jié)束時,所有資源被操作系統(tǒng)回收。
???????? Linux系統(tǒng)中主要包括下面幾種類型的過程:
1)交互式進程;
2)批處理進程;
3)守護進程;
?
Linux下的進程結(jié)構(gòu)
?????? 進程不但包括程序的指令和數(shù)據(jù),而且包括程序計數(shù)器和處理器的所有寄存器以及存儲臨時數(shù)據(jù)的進程堆棧。
?????? 因為Linux是一個多任務(wù)的操作系統(tǒng),所以其他的進程必須等到操作系統(tǒng)將處理器的使用權(quán)分配給自己之后才能運行。當(dāng)正在運行的進程需要等待其他的系統(tǒng)資源時,Linux內(nèi)核將取得處理器的控制權(quán),按照某種調(diào)度算法將處理器分配給某個等待執(zhí)行的進程。
??????? 在上面介紹程序和進程的區(qū)別時,我們看到進程除了內(nèi)存地址空間以外,還有個結(jié)構(gòu)體task_struct,內(nèi)核將所有進程存放在雙向循環(huán)鏈表(進程鏈表)中,鏈表的每一項就是這個結(jié)構(gòu)體task_struct,稱為進程控制塊的結(jié)構(gòu)。該結(jié)構(gòu)包含了與一個進程相關(guān)的所有信息,在linux內(nèi)核目錄下<include / Linux / sched.h>文件中定義。task_struct內(nèi)核結(jié)構(gòu)比較大,它能完整地描述一個進程,如進程的狀態(tài)、進程的基本信息、進程標(biāo)示符、內(nèi)存的相關(guān)信息、父進程相關(guān)信息、與進程相關(guān)的終端信息、當(dāng)前工作目錄、打開的文件信息,所接收的信號信息等。
???????
下面詳細講解task_struct結(jié)構(gòu)中最為重要的兩個域:stat (進程狀態(tài)) 和 pid (進程標(biāo)示符)。
1、進程狀態(tài)
Linux中的進程有以下幾種主要狀態(tài):運行狀態(tài)、可中斷的阻塞狀態(tài)、不可中斷的阻塞狀態(tài)、暫停狀態(tài)、僵死狀態(tài)、消亡狀態(tài),它們之間的轉(zhuǎn)換關(guān)系如下:
1)運行態(tài)(TASK_RUNNING):進程當(dāng)前正在運行,或者正在運行隊列中等待調(diào)度(排隊中);
2)等待態(tài)_可中斷(TASK_INTERRUPTIBLE):進程處于阻塞(睡眠sleep,主動放棄CPU)狀態(tài),正在等待某些事件發(fā)生或能夠占用某些資源。處于這種狀態(tài)下的進程可以被信號中斷。接收到信號或被顯式地喚醒呼叫(如調(diào)用wake_up系列宏:wake_up、wake_up_interruptible等)喚醒之后,進程將轉(zhuǎn)變?yōu)檫\行態(tài),繼續(xù)排隊等待調(diào)度;
3)登臺態(tài)_不可中斷(TASK_UNINTERRUPTIBLE):此進程狀態(tài)類似于可中斷的阻塞狀態(tài)(TASK_INTERRUPTIBLE),只是他不會處理信號,把信號傳遞到這種狀態(tài)下的進程不能改變它的狀態(tài),即不可被信號所中斷,不能被隨便調(diào)度。在一些特定的情況下(進程必須等待,知道某些不能被中斷的事件發(fā)生),這種狀態(tài)是很有用的。只有在它等待的事件發(fā)生時,進程才被顯示地喚醒呼叫喚醒,進程將轉(zhuǎn)變?yōu)檫\行態(tài),繼續(xù)排隊等待調(diào)度;
4)停止態(tài)(TASK_STOPPED),即暫停狀態(tài),進程的執(zhí)行被暫停,當(dāng)進程受到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU等信號,就會進入暫停狀態(tài),知道收到繼續(xù)執(zhí)行信號,轉(zhuǎn)變?yōu)檫\行態(tài),繼續(xù)排隊等待調(diào)度;
5)僵尸態(tài)(EXIT_ZOMBIE):子進程運行結(jié)束,父進程尚未使用wait 函數(shù)族(如使用wait()函數(shù))等系統(tǒng)調(diào)用來回收退出狀態(tài)。處于該狀態(tài)下的子進程已經(jīng)放棄了幾乎所有的內(nèi)存空間,沒有任何可執(zhí)行代碼,也不能被調(diào)度,僅僅在進程列表中(即task_struct)保留一個位置,記載該進程的退出信息供其父進程收集。即進程結(jié)束后,內(nèi)存地址空間被釋放、task_struct 成員被釋放,但task_struct 這個空殼還存在,它就是僵尸,這個僵尸我們用kill 是殺不掉的。所以,一般在子進程結(jié)束后,我們會對其進行回收?;厥盏姆椒ㄓ腥N:
1)誰創(chuàng)建誰回收,即用父進程來回收;
2)父進程不回收,通知內(nèi)核來回收;
3)由init 進程來回收,當(dāng)父進程先死掉,子進程成為孤兒進程,由 init 進程來認養(yǎng);
當(dāng)這三種條件都不滿足時,比如父進程不去回收子進程,自己卻未死掉,僵尸便會出現(xiàn),這是非常棘手的,可以通過殺死父進程來殺死僵尸(不推薦使用);?
2、進程標(biāo)識符
?????? Linux內(nèi)核通過唯一的進程標(biāo)示符PID來標(biāo)識每個進程。PID存放在task_strcut 的pid字段中。當(dāng)系統(tǒng)啟動后,內(nèi)核通常作為某一個進程的代表。一個指向task_struct 的宏 current 用來記錄正在運行的進程。current 進程作為進程描述符結(jié)構(gòu)指針的形式出現(xiàn)在內(nèi)核代碼中,例如,current->pid 表示處理器正在執(zhí)行的進程的PID。當(dāng)系統(tǒng)需要查看所有的進程時,則調(diào)用 for_each_process(宏),這將比系統(tǒng)搜索數(shù)組的速度要快的多。
???????在Linux 中獲得當(dāng)前進程的進程號(PID)和父進程號 (PPID) 的系統(tǒng)調(diào)用函數(shù)分別為 getpid() 和 getppid() 。
?3、進程的模式
? ? ? ? 進程的執(zhí)行模式分別為用戶模式和內(nèi)核模式。
? ? ? ? 在CPU的所有指令中,有一些指令是非常危險的,如果錯用,將導(dǎo)致整個系統(tǒng)崩潰。比如:清內(nèi)存、設(shè)置時鐘等。如果所有的程序都能使用這些指令,那么你的系統(tǒng)一天死機n回就不足為奇了。所以,CPU將指令分為特權(quán)指令和非特權(quán)指令,對于那些危險的指令,只允許操作系統(tǒng)及其相關(guān)模塊使用,普通的應(yīng)用程序只能使用那些不會造成災(zāi)難的指令。Intel的CPU將特權(quán)級別分為4個級別:RING0,RING1,RING2,RING3。
? ? ? ???linux的內(nèi)核是一個有機的整體。每一個用戶進程運行時都有一份內(nèi)核的拷貝,每當(dāng)用戶進程使用系統(tǒng)調(diào)用時,都自動地將運行模式從用戶態(tài)轉(zhuǎn)為內(nèi)核態(tài),此時進程在內(nèi)核的地址空間中運行。
? ? ? ? ?當(dāng)一個任務(wù)(進程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時,我們就稱進程處于內(nèi)核運行態(tài)(或簡稱為內(nèi)核態(tài))。此時處理器處于特權(quán)級最高的(0級)內(nèi)核代碼中執(zhí)行。當(dāng)進程處于內(nèi)核態(tài)時,執(zhí)行的內(nèi)核代碼會使用當(dāng)前進程的內(nèi)核棧。每個進程都有自己的內(nèi)核棧。
? ? ? ? ?當(dāng)進程在執(zhí)行用戶自己的代碼時,則稱其處于用戶運行態(tài)(用戶態(tài))。即此時處理器在特權(quán)級最低的(3級)用戶代碼中運行。當(dāng)正在執(zhí)行用戶程序而突然被中斷程序中斷時,此時用戶程序也可以象征性地稱為處于進程的內(nèi)核態(tài)。因為中斷處理程序?qū)⑹褂卯?dāng)前進程的內(nèi)核棧。這與處于內(nèi)核態(tài)的進程的狀態(tài)有些類似。
? ? ? ? ?內(nèi)核態(tài)與用戶態(tài)是操作系統(tǒng)的兩種運行級別,跟intel cpu沒有必然的聯(lián)系, 如上所提到的intel cpu提供Ring0-Ring3四種級別的運行模式,Ring0級別最高,Ring3最低。Linux使用了Ring3級別運行用戶態(tài),Ring0作為 內(nèi)核態(tài),沒有使用Ring1和Ring2。Ring3狀態(tài)不能訪問Ring0的地址空間,包括代碼和數(shù)據(jù)。Linux進程的4GB地址空間,3G-4G部 分大家是共享的,是內(nèi)核態(tài)的地址空間,這里存放在整個內(nèi)核的代碼和所有的內(nèi)核模塊,以及內(nèi)核所維護的數(shù)據(jù)。用戶運行一個程序,該程序所創(chuàng)建的進程開始是運行在用戶態(tài)的,如果要執(zhí)行文件操作,網(wǎng)絡(luò)數(shù)據(jù)發(fā)送等操作,必須通過write,send等系統(tǒng)調(diào)用,這些系統(tǒng)調(diào)用會調(diào)用內(nèi)核中的代碼來完成操作,這時,必須切換到Ring0,然后進入3GB-4GB中的內(nèi)核地址空間去執(zhí)行這些代碼完成操作,完成后,切換回Ring3,回到用戶態(tài)。這樣,用戶態(tài)的程序就不能 隨意操作內(nèi)核地址空間,具有一定的安全保護作用。
? ? ?處理器總處于以下狀態(tài)中的一種:
1、內(nèi)核態(tài),運行于進程上下文,內(nèi)核代表進程運行于內(nèi)核空間;
2、內(nèi)核態(tài),運行于中斷上下文,內(nèi)核代表硬件運行于內(nèi)核空間;
3、用戶態(tài),運行于用戶空間。
從用戶空間到內(nèi)核空間有兩種觸發(fā)手段:
1、用戶空間的應(yīng)用程序,通過系統(tǒng)調(diào)用,進入內(nèi)核空間。這個時候用戶空間的進程要傳遞很多變量、參數(shù)的值給內(nèi)核,內(nèi)核態(tài)運行的時候也要保存用戶進程的一些寄存器值、變量等。所謂的“進程上下文”,可以看作是用戶進程傳遞給內(nèi)核的這些參數(shù)以及內(nèi)核要保存的那一整套的變量和寄存器值和當(dāng)時的環(huán)境等。
2、硬件通過觸發(fā)信號,導(dǎo)致內(nèi)核調(diào)用中斷處理程序,進入內(nèi)核空間。這個過程中,硬件的一些變量和參數(shù)也要傳遞給內(nèi)核,內(nèi)核通過這些參數(shù)進行中斷處理。所謂的“中斷上下文”,其實也可以看作就是硬件傳遞過來的這些參數(shù)和內(nèi)核需要保存的一些其他環(huán)境(主要是當(dāng)前被打斷執(zhí)行的進程環(huán)境)。
? ?一個程序我們可以從兩種角度去分析。其一就是它的靜態(tài)結(jié)構(gòu),其二就是動態(tài)過程。下圖表示了用戶態(tài)和內(nèi)核態(tài)直接的關(guān)系(靜態(tài)的角度來觀察程序)
二、進程編程
1、fork() 函數(shù)
??????? 在Linux 中創(chuàng)建一個新進程的方法是使用fork() 函數(shù)。fork() 函數(shù)最大的特性就是執(zhí)行一次返回兩個值。
函數(shù)原型如下:
| 所需頭文件 | #include <sys/types.h> //提供類型pid_t定義 #include <unistd.h> |
| 函數(shù)原型 | pid_t? fork(void) |
| 函數(shù)返回值 | 0 :子進程 子進程PID(大于0的整數(shù)):父進程 -1 :出錯 |
fork() 函數(shù)用于從已存在的進程中創(chuàng)建一個新進程。新進程稱為子進程,而原進程稱為父進程。具體fork()函數(shù)究竟做了什么,我們先看這張圖:
?
這里我們可以看到,使用fork () 函數(shù)得到的子進程是父進程的一個復(fù)制品,它從父進程處繼承了整個進程的地址空間(注意:子進程有其獨立的地址空間,只是復(fù)制了父進程地址空間里的內(nèi)容),包括進程上下文、代碼段、進程堆棧、內(nèi)存信息、打開的文件描述符、信號處理函數(shù)、進程優(yōu)先級等。而子進程所獨有的只是它的進程號、資源使用和計時器等。
????? 因為子進程幾乎是父進程的完全復(fù)制,所以父子進程會運行同一個程序,這里,兩個進程都會從PC位置往下執(zhí)行;如何區(qū)分它們呢?父子進程一個很重要的區(qū)別是, fork()返回值不同。父進程中返回值是子進程的進程號,而子進程中返回0;所以在上圖中,兩個進程會通過判斷PID來選擇執(zhí)行的語句。
?????? 注意:子進程沒有執(zhí)行fork() 函數(shù),而是從fork() 函數(shù)調(diào)用的下一條語句開始執(zhí)行。
下面,寫一個fork()程序,來加深對fork()的理解:
[cpp]?view plaincopy執(zhí)行結(jié)果如下:
從結(jié)果我們可以發(fā)現(xiàn)幾個問題:
1)最后一行光標(biāo)在閃,是程序沒執(zhí)行完嗎?第三行中子進程打印前是bash,這是什么原因呢?
其實我們這里執(zhí)行的程序中有三個進程在執(zhí)行:父進程、子進程、bash。從打印結(jié)果中我們可以看到父進程先執(zhí)行完,然后是bash ,最后子進程執(zhí)行完,這里的光標(biāo)其實是bash的。所以,我們可以發(fā)現(xiàn):父進程、子進程誰先運行時不可知的,誰先運行有內(nèi)核調(diào)度來確定;
2)從打印結(jié)果中,可以看出父子進程打印出了各自的進程號和對應(yīng)變量的值,顯然global和test在父子進程間是獨立的,其各自的操作不會對對方的值有影響;
?
2、exec 函數(shù)族
??????? fork() 函數(shù)用于創(chuàng)建一個子進程,該子進程幾乎賦值了父進程的全部內(nèi)容。我們能否讓子進程執(zhí)行一個新的程序呢?exec 函數(shù)族就提供了一個在進程中執(zhí)行里一個程序的辦法。它可以根據(jù)指定的文件名或目錄找到可執(zhí)行文件,并用它來取代當(dāng)前進程的數(shù)據(jù)段、代碼段和堆棧段。在執(zhí)行完之后,當(dāng)前進程除了進程號外,其他的內(nèi)容都被替換掉了。所以,如果一個進程想執(zhí)行另一個程序,那么它就可以調(diào)用fork() 函數(shù)創(chuàng)建一個進程,然后調(diào)用exec家族中的任意一個函數(shù),這樣看起來就像執(zhí)行應(yīng)用程序而產(chǎn)生了一個新進程。
??????Linux 中并沒有exec() 函數(shù),而是有6個以 exec 開頭的函數(shù),下面是函數(shù)語法:
| 所需頭文件 | #include <stdio.h> |
| 函數(shù)原型 | int execl (const char *path,const char *arg,...); int execv (const char *path, char *const argv[]); int execle (const char *path,const char *arg,....,char *const envp[]); int execve(const char *path, char? const *argv[],char *const envp[]); int execlp (const char *file,const char *arg,...); int execvp (const char *file, char *const argv[]); |
| 函數(shù)返回值 | -1;出錯 |
exec 函數(shù)族使用區(qū)別
1)可執(zhí)行文件查找方式
表中的前四個函數(shù)的查找方式都是指定完整的文件目錄路徑,而最后兩個函數(shù)(以p 結(jié)尾的函數(shù))可以只給出文件名,系統(tǒng)會自動從環(huán)境變量“$PATH”所包含的路徑中進行查找。
2)參數(shù)表傳遞方式
兩種方式:逐個列舉或是將所喲參數(shù)通過指針數(shù)組傳遞
以函數(shù)名的第五位字母來區(qū)分,字母為" l ”(list) 的表示逐個列舉的方式;字母為"?v "(vertor) 的表示將所有參數(shù)構(gòu)成指針數(shù)組傳遞,其語法為 char *const argv[]
3)環(huán)境變量的使用
exec 函數(shù)族可以默認使用系統(tǒng)的環(huán)境變量,也可以傳入指定的環(huán)境變量,這里,以"e" (Enviromen) 結(jié)尾的兩個函數(shù)execle 、execve 就可以在 envp[] 中傳遞當(dāng)前進程所使用的環(huán)境變量;
exev使用示例:
[cpp]?view plaincopy執(zhí)行結(jié)果如下:
[cpp]?view plaincopy如果我們使用execvp,則
[cpp]?view plaincopy
3、exit() 和_exit()
1) exit()和_exit()函數(shù)說明
?? exit()和_exit() 都是用來終止進程的。當(dāng)程序執(zhí)行到exit()或_exit() 時,進程會無條件的停止剩下的所有操作,清除各種數(shù)據(jù)結(jié)構(gòu),并終止本進程的運行。但是,這兩個函數(shù)是有區(qū)別的:
可以看出exit() 是庫函數(shù),而_exit() 是系統(tǒng)調(diào)用;
_exit() 函數(shù)的作用最為簡單:直接使進程終止運行,清除其使用的內(nèi)存空間,并銷毀其在內(nèi)核中的各種數(shù)據(jù)結(jié)構(gòu);exit() 函數(shù)則在這些基礎(chǔ)上作了一些包裝,在執(zhí)行退出之前加了若干道工序。
二者函數(shù)描述如下:
| 所需頭文件 | exit()? :#include <stdlib.h> _exit():#include <unistd.h> |
| 函數(shù)原型 | exit()? :void exit(int status); _exit():void _exit(int status); |
| 函數(shù)傳入值 | status 是一個整形的參數(shù),可以利用這個參數(shù)傳遞進程結(jié)束時的狀態(tài)。 通常0表示正常結(jié)束;其他的數(shù)值表示出現(xiàn)了錯誤,進程非正常結(jié)束。在 實際編程時,可以用wait 系統(tǒng)調(diào)用接收子進程的返回值,進行相應(yīng)的處理。 |
其實,在main函數(shù)內(nèi)執(zhí)行return 語句,也會使進程正常終止;
exit(status) 執(zhí)行完會將終止?fàn)顟B(tài)(status)傳給內(nèi)核,內(nèi)核會將status傳給父進程的wait(&status),wait()會提取status,并分析;
?
4、wait 和waitpid()
wait() 函數(shù)???
調(diào)用該函數(shù)使進程阻塞,直到任一個子進程結(jié)束或者是該進程接受到了一個信號為止。如果該進程沒有子進程或其子進程已經(jīng)結(jié)束。wait 函數(shù)會立即返回。函數(shù)描述如下:
| 所需頭文件 | #include <sys/types.h> #includ <sys/wait.h> |
| 函數(shù)原型 | pid_t wait(int *status) |
| 函數(shù)參數(shù) | status是一個整型指針,指向的對象用來保存子進程退出時的狀態(tài) status 若為空,表示忽略子進程退出時的狀態(tài); status 若不為空,表示保存子進程退出時的狀態(tài); 另外,子進程的結(jié)束狀態(tài)可有Linux 中的一些特定的宏來測宏。 |
| 函數(shù)返回值 | 成功:子進程的進程號 失敗: -1 |
附:檢查wait 所返回的終止?fàn)顟B(tài)的宏
WIFEXTED (status)?:若為正常終止子進程返回的狀態(tài),則為真。對于這種情況可執(zhí)行 WEXITSTATUS (status) ,取子進程傳送給exit 參數(shù)的低八位;
(首先判斷子進程是否正常死亡,異常死亡是不會運行到exit()的,這時分析status 是無意義的;)
wait() 會回收任一一個先死亡的子進程;
下面看一個程序,wait() 與exit()的使用
[cpp]?view plaincopy執(zhí)行結(jié)果如下:
[cpp]?view plaincopy
waitpid() 函數(shù)
? ? ? ?waitpid() 函數(shù)和wait() 的作用是完全相同的,但waitpid 多出了兩個可有用戶控制的參數(shù)pid 和 options,從而為用戶編程提供了一種更為靈活的方式。waitpid 可以用來等待指定的進程,可以使進程不掛起而立刻返回。
wait(&status);等價于waitpid(-1, &status, 0);
其函數(shù)類型如下:
| 所需頭文件 | #include <sys/types.h> /* 提供類型pid_t的定義 */ #include <sys/wait.h> |
| 函數(shù)原型 | pid_t waitpid(pid_t pid,int *status,int options) |
| 函數(shù)傳入值 | pid > 0 時,只等待進程ID等于pid的子進程,不管其它已經(jīng)有多少子進程運行結(jié)束退出了, ? ? ? ? ? ? ? ? ? ? ? 只要指定的子進程還沒有結(jié)束,waitpid就會一直等下去。 pid = -1時,等待任何一個子進程退出,沒有任何限制, ? ? ? ? ? ? ? ? ? ? ?此時waitpid和wait的作用一模一樣。 pid = 0 時,等待同一個進程組中的任何子進程,如果子進程已經(jīng)加入了別的進程組, ? ? ? ? ? ? ? ? ? ? ? waitpid不會對它做任何理睬。 pid < -1時,等待一個指定進程組中的任何子進程,這個進程組的ID等于pid的絕對值。 |
| 函數(shù)傳入值 | WNOHANG ? ? ?如果沒有任何已經(jīng)結(jié)束的子進程則馬上返回, 不予以等待; WUNTRACED 如果子進程進入暫停執(zhí)行情況則馬上返回,但結(jié)束狀態(tài)不予以理會; 0 ? : 同wait() ,阻塞父進程,直到指定的子進程退出; |
| 函數(shù)返回值 | > 0 :已經(jīng)結(jié)束運行的子進程的進程號; 0 ? ?: 使用選項WNOHANG 且沒有子進程退出 -1 ? : 出錯 |
示例如下:
[cpp]?view plaincopy
執(zhí)行結(jié)果如下:
[cpp]?view plaincopy
總結(jié)
以上是生活随笔為你收集整理的Linux 系统应用编程——进程基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 把准脉搏 U-Mail邮件系统2014开
- 下一篇: 苹方字体 for linux,苹果苹方字