天天看點

fork和vfork函數、wait和waitpid函數

每一個程序都有一個非負整型表示的唯一程序ID。因為程序ID辨別符總是唯一的,常将其用作其他辨別符的一部分以保證其唯一性。雖然是唯一的,但是程序ID可以重用。大多數UNIX系統實作延遲重用算法,使得賦予建立程序的ID不同于最近終止程序所使用的ID。這防止了将新程序誤認為是使用同一ID的某個已終止的先前程序。

系統中有一些專用的程序,但具體細節因實作而異。ID為0的程序通常是排程程序,常常被稱為交換程序(swapper)。該程序是核心的一部分,它并不執行任何磁盤上的程式,是以也被稱為系統程序。程序ID 1通常是init程序,在自舉過程結束時由核心調用。

除了程序ID,每個程序還有一些其他的辨別符。如

#include <unistd.h>

pid_t getpid(void);   //傳回值:調用程序的程序ID

pid_t getppid(void); //傳回值:調用程序的父程序ID

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

uid_t geteuid(void); //傳回值:調用程序的有效使用者ID

gid_t getgid(void); //傳回值:調用程序的實際組ID

gid_t getegid(void); //傳回值:調用程序的有效組ID。

fork函數

一個現有程序可以調用fork函數建立一個新程序。

#include <unistd.h>

pid_t fork(void);

由fork建立的新程序被稱為子程序(child process)。fork函數被調用一次,但傳回兩次。兩次傳回的唯一差別是子程序的傳回值為0,而父程序的傳回值為新子程序的程序ID。将子程序ID傳回給父程序的理由是:因為一個程序的子程序可以有多個,并且沒有一個函數使一個程序可以獲得其所有子程序的程序ID。fork使子程序得到傳回值為0的理由:一個程序隻會有一個父程序,是以子程序總是可以調用getppid以獲得其父程序的程序ID(程序ID 0總是由核心交換程序使用,是以一個子程序的程序ID不可能為0)。

子程序和父程序繼續執行fork調用之後的指令。子程序是父程序的副本。例如,子程序獲得父程序資料空間、堆和棧的副本。注意,這是子程序所擁有的副本。父、子程序并不共享這些存儲空間部分。父、子程序共享正文段。

由于在fork之後經常跟随着exec,是以現在的很多實作并不執行一個父程序資料段、棧和堆的完全複制。作為替代,使用了寫時複制技術。這些區域由父、子程序共享,而且核心将它們的通路權限改變為隻讀的。一般來說,fork之後是父程序先執行還是子程序先執行是不确定的。這取決于核心所使用的排程算法。

fork有下面兩種用法:

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

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

vfork函數

vfork函數的調用序列和傳回值與fork相同,但兩者的語義不同。vfork用于建立一個新程序。而該新程序的目的是exec一個新程式。vfork和fork一樣都建立一個子程序,但是它并不将父程序的位址空間完全複制到子程序中,因為子程序會立即調用exec,于是也就不會通路該位址空間。vfork和fork之間的另一個差別是:vfork保證子程序先執行,在它調用exec或exit之後父程序才可能被排程運作。

當一個程序正常或異常終止時,核心就向其父程序發送SIGCHLD信号。子程序終止是個異步事件,是以這種信号也是核心向父程序發的異步通知。調用wait或waitpid的程序可能發生的情況:

1.如果其所有子程序都還在運作,則堵塞。

2.如果一個子程序已終止,正等待父程序擷取其終止狀态,則取得該子程序的終止狀态立即傳回。

3.如果它沒有任何一個子程序,則立即出錯傳回。

#include <sys/wait.h>

pid_t wait(int *statloc);

pid_t waitpid(pid_t pid,int *statloc,int options);

兩個函數的傳回值:若成功則傳回程序的ID ,若出錯則傳回-1。

這兩個函數的差別如下:

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

2.waitpid并不等待在其調用之後的第一個終止子程序,它有若幹個選項,可以控制它所等待的程序。

這兩個函數的參數statloc是一個整型指針。如果statloc不是一個空指針,則終止程序的終止狀态就存放在它所指向的單元内。依據傳統,這兩個函數傳回的整型狀态字是有現實定義的。其中,某些位表示退出狀态(正常傳回),其他位則訓示信号編号(異常傳回),有一位訓示是否産生一個core檔案等。POSIX.1規定終止狀态用定義在<sys/wait.h>中的各個宏檢視。有四個互斥的宏可用來取得程序終止的原因,它們的名字都以WIF開始。

wait和waitpid傳回的終止狀态的宏

WIFEXITED(status),若為正常終止子程序傳回的狀态,則為真。對于這種情況可執行WEXITSTATUS(status),取子程序傳送給exit、_exit和_Exit參數的低8位。

WIFSIGNALED(status),若為異常終止子程序傳回的狀态,則為真(接收到一個不捕捉的信号)。對于這種情況,可執行WTERMSIG(status),取使子程序終止的信号編号。

WIFSTOPED(status),若為目前暫停子程序的傳回狀态,則為真。對于這種情況,可執行WSTOPSIG(status),取使子程序暫停的信号編号。

WIFCONTINUED(status),若在作業控制暫停後已經繼續的子程序傳回的狀态,則為真。

如果一個程序有幾個子程序,那麼隻要有一個子程序終止,wait就傳回。如果要等待一個指定的程序終止,則使用waitpid。對于waitpid函數中pid參數的作用:

pid==-1;等待任一子程序。就這一方面而言,waitpid和wait等效。

pid>0;等待其程序ID 與pid相等的子程序

pid==0;等待其組ID 等于調用程序組ID的任一子程序。

pid<-1;等待其組ID等于pid絕對值的任一子程序。

繼續閱讀