APUE 程序控制 中

2021-09-08 12:22:57 字數 4037 閱讀 2317

當乙個程序正常或異常終止時會向父程序傳送sigchld訊號。對於這種訊號系統缺省會忽略。呼叫wait/waidpid的程序可能會:

#include #include 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規定終止狀態用定義在中的各個巨集來檢視。有三個互斥的巨集可用來取得程序終止的原因,它們的名字都已wif開始。基於這三個巨集中哪乙個值是真,就可選用其他巨集(這三個巨集之外的其他巨集)來取得終止狀態、訊號編號等。

下面的程式中pr_exit函式使用上表中的巨集以列印程序的終止狀態。

#include #include #include #include void pr_exit(int status)  else if (wifsignaled(status))  else if (wifstopped(status))  } int main(void)  else if (pid == 0)  if (wait(&status) != pid)  pr_exit(status); if ((pid = fork()) < 0)  else if (pid == 0)  if (wait(&status) != pid)  pr_exit(status); if ((pid = fork()) < 0)  else if (pid == 0)  if (wait(&status) != pid)  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) else if (pid == 0) tell_child(pid); wait_child(); exit(0);

當程序呼叫exec函式時,該程序完全由新程序代換,而新程式則從其main函式開始執行。因為呼叫exec並不建立新程序,所以前後的程序id不會改變。exec只是用另乙個程式替換了當前程序的正文、資料、堆和棧段。

#include int execl(const char *pathname, const char *arg0, ... /* (char *) 0 */); int execv(const char *pathname, char *const ar**); int execle(const char *pathname, const char *arg0, ... /* (char *)0, char *const envp */); int execve(const char *pathname, char *const ar**, char *const envp); int execlp(const char *filename, const char *arg0, ... /* (char *) 0 */); int execvp(const char *filename, char *const ar**); 返回值:出錯-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個是庫函式

APUE學習 程序控制

程序識別符號在系統中是唯一的。unix採用延遲技術來分配pid。因為,如果乙個程序終止了,馬上把他的pid分配給新的程序,而且這個新程序與舊程序要做一樣的事。那麼別人就不知道這是新程序還是舊程序在做了。類似於tcp4次揮手的最後一步。還有一些特殊的程序,例如pid為0的程序,這個可以看成是乙個核心的...

APUE學習筆記 程序控制

1.getpid可以獲取程序id。getppid可以獲取程序呼叫程序的id。2.fork函式 一次呼叫,兩次返回 返回0是在子程序中。返回其他值在父程序中。如果大於0為子程序id 否則失敗。3.子程序獲得了父程序的資料空間 data,bss 堆,棧的副本。程序之間共享正文段。4.目前的實現一般不進行...

程序及程序控制

學習程序之前,先了解一下程式 所謂程式就是指編譯好的二進位制檔案,在磁碟上,不占用系統資源 cpu 記憶體.而程序是與作業系統相關,是指在記憶體中執行起來的程式,占用一些系統資源,每當乙個程式執行,就相應產生乙個程序。程序的一些相關資訊被放在乙個叫程序控制塊的資料結構中,稱之為pcb。linux下的...