天天看點

程序建立——fork()

1. 函數原型

pid_t fork(void);
           

pid_t實質是int類型,Linux核心2.4.0定義:

typedef int _kernel_pid_t;
typedef _kernel_t pid_t;
           

2. 函數功能

fork()函數會新生成一個程序,調用fork()函數的程序為父程序,新生成的程序為子程序。

fork()函數調用一次,傳回兩次,在父程序傳回子程序的pid,在子程序傳回0,失敗則傳回-1。

3. 函數用法

#include <unistd.h>		//頭檔案
int main()
{
	int p=fork();
	if(p==0)
	{
		func...;		//子程序
	}
	else
	{
		func...			//父程序
	}
	return 0;
}
           

4. 寫時拷貝

fork()函數在生成子程序時用到了寫時拷貝技術。其實作原理是,不執行一個父程序資料段、棧區、堆區的完全複制,這些區域将由父子程序共享,核心修改其通路權限為隻讀。如果父子程序中任何一個試圖修改這些這些區域的資料,則核心隻為被修改資料的相應記憶體制作一個副本,并且以虛拟存儲系統中的“一頁”為機關複制。

5. 核心實作原理

Linux通過clone()系統調用函數實作fork(),調用流程如下圖:

程式建立——fork()

copy_process()函數的工作流程如下:

(1)調用dup_task_struct()為新程序建立一個核心棧、thread_info與task_struct結構,這些資料結構的值與目前程序的值相同,此時父子程序的描述符完全相同;

(2)檢查建立新的子程序之後,目前使用者所擁有的程序數目是否超出為其配置設定的資源限制;

(3)程序描述符内的成員都被清0或設為初始值,以保證将父子程序區分開來,但程序描述符中的大多數資料都是共享的;

(4)子程序的狀态被設定為TASK_UNINTERRUPTIBLE,以保證其不會投入運作;

(5)copy_process()函數調用copy_flags()更新task_struct的flags成員,辨別程序是否擁有超級使用者權限的PF_SUPERPRIV标志被清0,辨別程序還未調用exec()函數的PF_FORKNOEXEC标志被重置;

(6)調用get_pid()為新程序擷取一個有效的PID;

(7)根據傳遞給clone()的參數标志,copy_process()拷貝或共享打開的檔案、檔案系統資訊、信号處理函數、程序位址空間和指令空間等。一般這些資源被同一個程序中的所有線程共享,但是這些資源對每個程序都是互相獨立的,是以需要拷貝;

(8)父子程序平分剩餘的時間片;

(9)copy_process()做掃尾工作并傳回一個指向子程序的指針。

Linux核心應該保證新建立的子程序先執行,一般子程序都會馬上調用exec()函數,以此來避免寫時拷貝的額外開銷,如果父程序先執行,則有可能會向記憶體空間寫入資料,進而發生寫時拷貝,造成額外的資源開銷。

6. vfork()函數

函數原型:

pid_t vfork(void)
           

特點:vfork()函數保證子程序先執行,隻有當調用exec()或_exit()時父程序才會執行

7. 僵屍程序

僵屍程序也稱為僵屍程序,是因為父程序未結束,子程序結束,并且父程序未擷取到子程序的退出狀态,這樣的程序,程序主體空間已經釋放,但程序控制塊(PCB)還未釋放。

處理辦法:

(1)在父程序中調用wait()或waitpid()擷取子程序的退出狀态,這種方式可能會導緻父程序在wait()或waitpid()阻塞運作,直到子程序退出;

(2)父程序調用signal(SIGCHLD,SIG_IGN)來忽略SIGCHLD信号,這樣子程序結束後會由核心釋放其PCB和其他資源;

8. 孤兒程序

孤兒程序是指父程序結束,子程序未結束,這樣的程序被稱為孤兒程序。孤兒程序會被系統程序init收養,并為他們完成狀态收集工作

9. 守護程序

守護程序也稱為精靈程序,常常在系統啟動時自啟,僅在系統關閉時才終止,生存周期較長,一般都是在背景運作。

ps -axj可以用來檢視系統守護程序,其中最為常見的init程序,負責各運作層次間的系統指令

繼續閱讀