當一個進程正常或異常終止時會向父進程發送SIGCHLD信號。對於這種信號系統默認會忽略。調用wait/waidpid的進程可能會:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
返回值: 成功返回進程ID, 出錯-1.
這兩個函數區別:
如果調用者阻塞而且它有多個子進程,則在其一個子進程終止時,wait就立即返回。因為wait返回子進程ID,所以調用者知道是哪個子進程終止了。
參數statloc是一個整型指針。如果statloc不是一個空指針,則終止狀態就存放到它所指向的單元內。如果不關心終止狀態則將statloc設為空指針。
這兩個函數返回的整型狀態由實現定義。其中某些位表示退出狀態(正常退出),其他位則指示信號編號(異常返回),有一位指示是否產生了一個core文件等等。POSIX.1規定終止狀態用定義在<sys/wait.h>中的各個宏來查看。有三個互斥的宏可用來取得進程終止的原因,它們的名字都已WIF開始。基於這三個宏中哪一個值是真,就可選用其他宏(這三個宏之外的其他宏)來取得終止狀態、信號編號等。
下面的程序中pr_exit函數使用上表中的宏以打印進程的終止狀態。
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
void pr_exit(int status)
{
if (WIFEXITED(status)) {
printf("normal termination, exit status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("abnormal termination, signal number = %d\n", WTERMSIG(status),
#ifdef WCOREDUMP
WCOREDUMP(status) ? "(core file generated)" : "");
#else
"");
#endif
} else if (WIFSTOPPED(status)) {
printf("child stopped, signal number = %d\n", WSTOPSIG(status));
}
}
int main(void)
{
pid_t pid;
int status;
if ((pid = fork()) < 0) {
fprintf(stderr, "fork error");
} else if (pid == 0) {
exit(7);
}
if (wait(&status) != pid) {
fprintf(stderr, "wait error");
}
pr_exit(status);
if ((pid = fork()) < 0) {
fprintf(stderr, "fork error");
} else if (pid == 0) {
abort();
}
if (wait(&status) != pid) {
fprintf(stderr, "wait error");
}
pr_exit(status);
if ((pid = fork()) < 0) {
fprintf(stderr, "fork error");
} else if (pid == 0) {
status /= 0;
}
if (wait(&status) != pid) {
fprintf(stderr, "wait error");
}
pr_exit(status);
return 0;
}
編譯運行結果:
wait是只要有一個子進程終止就返回,waitpid可以指定子進程等待。對於waitpid的pid參數:
對於wait,其唯一的出錯是沒有子進程(函數調用被一個信號中斷,也可能返回另一種出錯)。對於waitpid, 如果指定的進程或進程組不存在,或者調用進程沒有子進程都能出錯。 options參數使我們能進一步控制waitpid的操作。此參數或者是0,或者是下表中常數的逐位或運算。
當多個進程都企圖對某共享數據進行某種處理,而最後的結果又取決於進程運行的順序,則我們認為這發生了競態條件(race condition)。如果在fork之後的某種邏輯顯式或隱式地依賴於在fork之後是父進程先運行還是子進程先運行,那麼fork函數就會是競態條件活躍的孽生地。
如果一個進程希望等待一個子進程終止,則它必須調用wait函數。如果一個進程要等待其父進程終止,則可使用下列形式的循環:
while(getppid() != 1)
sleep(1);
這種形式的循環(稱為定期詢問(polling))的問題是它浪費了CPU時間,因為調用者每隔1秒都被喚醒,然後進行條件測試。
為了避免競態條件和定期詢問,在多個進程之間需要有某種形式的信號機制。在UNIX中可以使用信號機制,各種形式的進程間通信(IPC)也可使用。
在父、子進程的關系中,常常有以下情況:在fork之後,父、子進程都有一些事情要做。例如:父進程可能以子進程ID更新日志文件中的一個記錄,而子進程則可能要為父進程創建一個文件。在本例中,要求每個進程在執行完它的一套初始化操作後要通知對方,並且在繼續運行之前,要等待另一方完成其初始化操作。這種情況可以描述為如下:
TELL_WAIT();
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) {
TELL_PARENT(getppid());
WAIT_PARENT();
exit(0);
}
TELL_CHILD(pid);
WAIT_CHILD();
exit(0);
當進程調用exec函數時,該進程完全由新進程代換,而新程序則從其main函數開始執行。因為調用exec並不創建新進程,所以前後的進程ID不會改變。exec只是用另一個程序替換了當前進程的正文、數據、堆和棧段。
#include <unistd.h>
int execl(const char *pathname, const char *arg0, ... /* (char *) 0 */);
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, ... /* (char *)0, char *const envp[] */);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, ... /* (char *) 0 */);
int execvp(const char *filename, char *const argv[]);
返回值:出錯-1,若成功不返回
這些函數之間的第一個區別是前四個取路徑名作為參數,後兩個取文件名作為參數。當制定filename作為參數時:
如果excelp和execvp中的任意一個使用路徑前綴中的一個找到了一個可執行文件,但是該文件不是機器可執行代碼文件,則就認為該文件是一個shell腳本,於是試著調用/bin/sh,並以該filename作為shell的輸入。
第二個區別與參數表的傳遞有關(l 表示表(list),v 表示矢量(vector))。函數execl、execlp和execle要求將新程序的每個命令行參數都說明為一個單獨的參數。這種參數表以空指針結尾。另外三個函數execv,execvp,execve則應先構造一個指向個參數的指針數組,然後將該數組地址作為這三個函數的參數。
最後一個區別與向新程序傳遞環境表相關。以 e 結尾的兩個函數excele和exceve可以傳遞一個指向環境字符串指針數組的指針。其他四個函數則使用調用進程中的environ變量為新程序復制現存的環境。
六個函數之間的區別:
每個系統對參數表和環境表的總長度都有一個限制。當使用shell的文件名擴充功能產生一個文件名表時,可能會收到此值的限制。例如,命令:
grep _POSIX_SOURCE /usr/include/*/*.h
在某些系統上可能產生下列形式的shell錯誤。
arg list too long
執行exec後進程ID沒改變。除此之外,執行新程序的進程還保持了原進程的下列特征:
對打開文件的處理與每個描述符的exec關閉標志值有關。進程中每個打開描述符都有一個exec關閉標志。若此標志設置,則在執行exec時關閉該文件描述符,否則該描述符仍打開。除非特地用fcntl設置了該標志,否則系統的默認操作是在exec後仍保持這種描述符打開。
POSIX.1明確要求在exec時關閉打開目錄流。這通常是由opendir函數實現的,它調用fcntl函數為對應於打開目錄流的描述符設置exec關閉標志。
在exec前後實際用戶ID和實際組ID保持不變,而有效ID是否改變則取決於所執行程序的文件的設置-用戶-ID位和設置-組-ID位是否設置。如果新程序的設置-用戶-ID位已設置,則有效用戶ID變成程序文件的所有者的ID,否則有效用戶ID不變。對組ID的處理方式與此相同。
在很多UNIX實現中,這六個函數只有一個execve是系統調用。另外5個是庫函數
http://xxxxxx/Linuxjc/1186728.html TechArticle