Linux環境編程之進程(四):建立新進程、執行程式和進程終止

來源:互聯網
上載者:User

標籤:進程建立   進程終止   

引言:

對於每個進程,都有一個非負整數表示的唯一進程ID。雖然進程的ID是唯一的,但卻是可重用的。系統中有一些專用的進程。如ID為0的進程通常是調度進程,也成交換進程或系統進程(它是核心進程)。進程ID為1通常是init進程,它是一個普通的使用者進程。一些與進程ID有關的函數:

#include <unistd.h>

pid_t getpid(void);   //返回值:調用進程的進程ID

pit_t getppid(void); //返回值:調用進程的父進程ID

uid_t getuid(void); //返回值:調用進程的實際使用者ID

uid_t geteuid(void); //返回值:調用進程的有效組ID

gid_t getgid(void); //返回值:調用進程的有效使用者ID

git_t getegid(void); //返回值:調用進程的有效組ID

(一)

一個現有進程可以通過調用fork函數建立一個新的進程。由fork建立的新進程被稱為子進程。fork函數被調用一次,但返回兩次。兩次返回的唯一區別是子進程的返回值是0,而父進程的返回值則是新的子進程的進程ID。子進程和父進程繼續執行fork調用之後的指令。子進程是父進程的副本。例如,子進程獲得父進程資料空間、堆和棧的副本。注意,這是子進程所擁有的副本。父子進程並不共用這些儲存空間部分。父子進程共用本文段。由於在fork之後,經常跟隨者exec,所以現在的很多實現並不執行一個父進程資料區段、棧和堆的完全複製。作為替代,使用寫時複製技術。

fork的一般使用形式如下:

if((pid = fork()) < 0){ // 用fork建立新進程printf("fork error");}else if(pid == 0){//子進程的操作}else{//父進程的操作}

父、子進程之間的區別是:

fork的返回值不同;進程ID不同;兩個進程具有不同的父進程ID:子進程的父進程ID是建立它的進程的ID,而父進程的父進程ID則不變;子進程的tms_utime、tms_stime、tms_cutime和tms_ustime均被設定為0。父進程設定的檔案鎖不會被子進程繼承。子進程的未處理的鬧鐘被清除。子進程的未處理訊號集設定為空白集。

子進程除了繼承了父進程開啟的檔案外,還包括:實際使用者ID、實際組ID、有效使用者ID、有效組ID,附加組ID、進程組ID、會話ID,控制終端、儲存映射等等。

通常情況下fork都會成功,但也有可能失敗。使fork失敗的兩個主要原因是:1、系統中已經有了太多的進程,2、該實際使用者ID的進程總數超過了系統限制。

fork有下面兩種用法:

1、一個父進程希望複製自己,使父、子進程同時執行不同的程式碼片段。這在網路服務進程中是常見的——父進程等待用戶端的服務要求。當這種請求到達時,父進程調用fork,使子進程處理此請求。父進程則繼續等待下一個服務要求到達。

2、一個進程要執行一個不同的程式。這對shell是常見的情況。這種情況下,子進程從fork返回後立即調用exec。


除了fork建立一個新進程外還有vfork同樣用來建立一個新進程:

vfork函數的調用序列和返回值與fork相同,但兩者的語義有如下幾點不同:

1、vfork用於建立一個新進程,而該新進程的目的是exec一個新程式。

2、vfork與fork一樣都建立一個子進程,但它並不將父進程的地址空間完全複製到子進程中,因為子進程會立即調用exec(或exit),於是也就不會存訪該地址空間。

3、vfork與fork之間的另一個區別是:vfork保證子進程先運行,在它調用exec或exit之後父進程才可能運行。

(二)

進程是有生命週期的,從其被建立到終止,就是其生命週期。進程的終止有8種方式,5種正常終止方式、3種異常終止方式。不管進程如何終止,最後都會執行核心中的同一段代碼。這段代碼為相應進程關閉所有開啟描述符,釋放它所使用的儲存空間等。

不管是正常終止還是異常終止,我們都希望終止進程能夠通知其父進程它是如何終止的。對於三個終止函數(exit、_exit和_Exit),實現這一點的方法是,將其退出狀態作為參數傳送給函數。在異常終止情況下,核心(而不是進程本身)產生一個指示其異常終止原因的終止狀態。在任意一種情況下,該終止進程的父進程都能用wait或waitpid函數去的終止狀態。注意:這裡使用了“退出狀態”和“終止狀態”兩個術語,以表示有所區別。在最後調用_exit時,核心將退出狀態轉換成終止狀態。

關於進程的終止及退出狀態,要注意一下幾點:

1、子進程在父進程調用fork後產生,子進程將其終止狀態返回給父進程。但如果父進程在子進程之前終止,則,對於父進程已經終止的所有進程,他們的父進程都改變為init進城。稱這些進程被init進程領養。操作過程如下:在一個進程終止時,核心逐個檢查所有活動進程,以判斷它是否是正要終止進程的子進程,如果是,則將該進程的父進程ID更改為1。

2、如果子進程在父進程之前終止,那麼父進程又如何能在做相應檢查時得到子進程的終止狀態呢?答:核心為每個終止進程儲存了一定量的資訊,所以當終止進程的父進程調用wait或waitpid時,可以得到這些資訊。這些資訊至少包括進程ID、該進程的終止狀態、以及該進程使用的CPU時間總量。核心可以釋放終止進程所使用的所有儲存區,關閉其所有開啟檔案。一個已經終止、但是其父進程尚未對其進行善後處理的進程稱為僵死進程。

3、一個由init進程領養的進程終止時會發生什麼?它會不會變成一個僵死進程?不會。因為init被編寫成無論何時只要有一個子進程終止,init就會調用一個wait函數去的其終止狀態。當提及“一個init的子進”時,這指的可能是init直接產生的進程,也可能是其父進程已終止,由init領養的進程。

(三)

當一個進程正常或異常終止時,核心就向其父進程發送SIGCHLD訊號。因為子進程終止是個非同步事件,所以這種訊號也是核心向父進程發的非同步通知。父進程可以選擇忽略該訊號,或者提供一個該訊號發生時即被調用執行的寒素(訊號處理常式)。系統預設是忽略它。現在需要知道的是調用wait或waitpid的進程可能發生什麼情況:

1、如果其所有子進程都還在運行,則阻塞。

2、如果一個子進程已終止,正等待父進程擷取其終止狀態,則取得該子進程的終止狀態立即返回。

3、如果它沒有任何子進程,則立即出錯返回。

       #include <sys/types.h>
       #include <sys/wait.h>
       pid_t wait(int *status);
       pid_t waitpid(pid_t pid, int *status, int options);  // 返回值:若成功則返回進程ID,0,若出錯則返回-1

兩個函數的區別是:

1、在一個子進程終止前,wait使其調用者阻塞,而waitpid有一個選項,可使調用者不阻塞。

2、waitpid並不等待在其調用之後的第一個終止子進程,它由若干選項,可以控制它所等待的進程。

函數的參數status是一個整型指標。如果status不是一個null 指標,則終止進程的終止狀態就存放在它所指向的單元內。如果不關心終止狀態,則可將該參數指定為空白指標。檢查wait或waitpid所返回的終止狀態的宏有4個:WIFEXITED(status)、WIFSIGNALED(status)、WIFSTOPPED(status)、WIFONTINUED(stauts)。

waitpid函數提供了wait函數沒有提供的三個功能:

1、waitpid可等待一個特定的進程,而wait則返回任一終止子進程的狀態。

2、waitpid提供了一個wait的非阻塞版本。有時使用者希望取得一個子進程的狀態,但不想阻塞。

3、waitpid支援作業控制。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.