《LINUX學習:進程管理之fork函數》要點:
本文介紹了LINUX學習:進程管理之fork函數,希望對您有用。如果有疑問,可以聯系我們。
#include <unistd.h>
#include <sys/types.h>
pid_t fork(void);
fork函數在父進程中返回子進程的pid,在子進程中返回0.注意在子進程中返回的0,并不是子進程的pid,子進程的pid在父進程的返回值中保存.而子進程的返回值是為了標識它是子進程,用來區分父子進程的.那么為什么這樣設計父子進程的返回值呢?我的理解是這樣的:第一,對于父進程來說,它可能同時有多個子進程,并且沒有一個函數可以獲得所有子進程的pid,所以它需要知道每個子進程的pid,這樣便于管理各個子進程;第二,對于子進程來說,它父進程只能有一個,所以它可以不必實現和父進程類似的機制,獲取父進程的pid,它可以通過getpid函數獲取父進程的pid.而在內核中,進程pid為0,總是在內核交換進程時使用,所以進程的真實pid不可能為0.
fork函數并不是在被調用時,為子進程copy一份父進程的副本,而是將父進程數據段、堆區和棧區的權限改變為只讀.當父子進程中任何一個進程要修改該區域的值時,內核只會為修改區域的那塊內存制作一個副本,通常是虛擬內存中的一頁.這樣的機制無需copy所有的父進程資源(這些資源在子進程中不一定有用),提高了程序的效率.但要注意,父子進程共享代碼段.
1)數據段、堆區、棧區、參數表和環境表和上文說的一樣,遵循讀時共享,寫時復制的機制;
2)環境表中只能改變其本身進程和其子進程的環境變量,無法改變其父進程的環境變量;
3)像是實際用戶ID、實際組ID、有效用戶ID、有效組ID、進程組ID和附屬ID,這些都是進程的屬性,所以存在于PCB中,每個進程都有自己的PCB節點,其中部分數據是繼承父進程的.當然pid不會.
4)對于文件鎖,子進程是不會被繼承的.子進程雖然復制了文件描述符表,但都指向同一個文件表,當然了,文件表也就指向了同一個i節點.在i節點中保存著和文件鎖相關的鏈表頭節點(struct lockf)的地址.i節點資源不屬于進程資源,而是文件資源,所以不會繼承文件鎖.
安插信號:利用signal函數安插SIGCHLD信號.因為在子進程結束后,父進程會收到該信號.再自己寫個回調函數,在函數中調用wait或waitpid函數,回收進程表資源;如果父進程對子進程結束不感興趣,則可以利用“signal(SIGCHLD,SIG_IGN)”,將回收子進程資源的工作交給內核來做;但要注意,SIGCHLD信號是傳統的不可靠信號,信號處理函數執行期間會暫時阻塞,因此,在這期前,如果又有了SIGCHLD信號,則會被拋棄,即無法處理多個SIGCHLD信號.所以信號處理函數的正確寫法是:
void handler(int signs)
{
int tmp_errno=errno;
while(waitpid(-1,&status,WNOHANG)>0)
{
//處理返回信息
}
errno=tmp_errno;
}
但仍要注意的是,當waitpid返回值為-1時,會改變全局變量errno的值,如果這是在主程序中檢測errno的值時,就很有可能發生沖突.因此,在進入信號處理函數之前要保存errno 的值,最后再回復errno的值.利用孤兒進程的機制:在創建的子進程中在調用fork函數,創建一個孫子進程,然后子進程終止,那么孫子進程就會被init進程收養.再當孫子進程結束時,回收進程資源的工作就交由了init進程去做.當然,有人會問,那么子進程結束后它的進程資源誰去回收?我說,那當然是父進程回收.這個時候能用父進程去回收子進程資源,是因為這是父子進程已經不在時異步的關系了.換句直白點的話說就是,父進程知道子進程在不久的將來一定會結束.回頭想想,安插信號的辦法的本質,不就是通過信號捕捉,確定了子進程會在不久的將來會結束嗎?即在調用wait和外套pid時,不會阻塞等待很長時間就能返回,不會影響父進程自身的工作.
注意:安插信號和利用孤兒進程的機制來避免僵尸進程的辦法,不僅僅能避免僵尸進程,還能不影響父進程本身的工作任務,這一點是非常有用的.
本文永久更新鏈接地址:
維易PHP培訓學院每天發布《LINUX學習:進程管理之fork函數》等實戰技能,PHP、MYSQL、LINUX、APP、JS,CSS全面培養人才。
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/7685.html