天天看點

Linux建立程序fork和vfork函數分析程序建立之fork函數 程序建立之vfork函數

程序建立之fork函數

頭檔案:#include<unistd.h>

               #include <sys/types.h>

函數原型:pid_t fork(void);             pid_t實際就是int,在sys/types.h中被宏定義的

傳回值:1.建立程序失敗---傳回-1(失敗原因有兩個:1)目前的程序數已經達到了系統規定的上限,這時errno的值被設定為EAGAIN。2)系統記憶體不足,這時errno的值被設定為ENOMEM)

                2.建立成功:子程序傳回0,父程序傳回子程序ID

函數說明:一個現有的程序調用fork函數建立一個新的程序,新程序稱之為子程序,調用fork函數的程序稱為父程序。子程序是父程序的副本,它将獲得父程序資料空間、堆棧等資源的副本。子程序持有的是上述存儲空間的“副本”,父子程序間不共享這些存儲空間。linux将複制父程序的位址空間内容給子程序,是以,子程序有了獨立的位址空間。在Linux下,一個程序在記憶體裡有三部分的資料---"代碼段"、"堆棧段"和"資料段"。"代碼段",存放程式代碼的資料,假如機器中有數個程序運作相同的一個程式,那麼它們就可以使用相同的代碼段。"堆棧段"存放的是子程式的傳回位址、子程式的參數以及程式的局部變量等。“資料段”則存放程式的全局變量,常數以及動态資料配置設定的資料空間(比如用malloc之類的函數取得的空間)。調用fork函數,父子程序共用代碼段,但是資料段和堆棧段是獨立的。也就是說資料段和堆棧段的資料是互相不影響的

示例代碼:

#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

static int s_nTmp1 = 0;
static int s_nTmp2 = 0;

int main(void)
{
	int nTmp = 0;
	pid_t pid = 0;

	printf("Before fork\n");

	pid = fork();

	printf("This is a test\n");
	
	if(pid > 0)
	{
		nTmp   = 1;
		s_nTmp1 = 1;
		s_nTmp2 = 1;
		printf("這是父程序,它的pid=%d,他的子程序的pid=%d\n", getpid(), pid);
		printf("父程序--nTmp = %d\n", nTmp);
		printf("父程序--s_nTmp1 = %d\n", s_nTmp1);
		printf("父程序--s_nTmp2 = %d\n", s_nTmp2);
		while(1);
	}	
	else
	{
		printf("這是子程序,它的pid=%d,它的父程序的pid=%d\n", getpid(), getppid());
		printf("子程序--nTmp = %d\n", nTmp);
		printf("子程序--s_nTmp1 = %d\n", s_nTmp1);
		printf("子程序--s_nTmp2 = %d\n", s_nTmp2);		
		while(1);
	}
	
	return 0;
}
           

執行結果:

Before fork

This is a test

這是父程序,它的pid=2515,他的子程序的pid=2516

父程序--nTmp = 1

父程序--s_nTmp1 = 1

父程序--s_nTmp2 = 1

This is a test

這是子程序,它的pid=2516,它的父程序的pid=2515

子程序--nTmp = 0

子程序--s_nTmp1 = 0

子程序--s_nTmp2 = 0

結果分析:執行結果中可以看到,在fork之前的代碼隻會被執行一次(Before fork隻會被列印一次),而fork之後的代碼都會被執行兩次(This is a test列印了兩次),這是因為父程序和子程序共享代碼段,各執行了一次fork之後的代碼。在fork之後,父程序和子程序都有自己獨立資料段和堆棧段(子程序的資料段和堆棧段都是父程序的拷貝),是以,在任意程序中(無論是父程序還是子程序)對全局變量(資料段)和局部變量(堆棧段)的操作都不會影響到另一個程序中的值。

注意點:

1)執行結果也可能是

Before fork

This is a test

這是子程序,它的pid=2516,它的父程序的pid=2515

子程序--nTmp = 0

子程序--s_nTmp1 = 0

子程序--s_nTmp2 = 0

This is a test

這是父程序,它的pid=2515,他的子程序的pid=2516

父程序--nTmp = 1

父程序--s_nTmp1 = 1

父程序--s_nTmp2 = 1

即先執行父程序還是子程序是未知的,這點千萬要注意。(無論哪個先執行,fork之前的代碼肯定都在之前)

2)在程序的結束時,也可以不加while(1),此時會退出一個程序,然後又繼續執行另一個程序,即

Before fork

This is a test

這是父程序,它的pid=2635,他的子程序的pid=2636

父程序--nTmp = 1

父程序--s_nTmp1 = 1

父程序--s_nTmp2 = 1

[email protected]:~/home/progress$ This is a test

這是子程序,它的pid=2636,它的父程序的pid=1

子程序--nTmp = 0

子程序--s_nTmp1 = 0

子程序--s_nTmp2 = 0

此時父程序先退出,然後子程序繼續執行

程序建立之vfork函數

頭檔案:同fork函數

函數原型:pid_t vfork(void);             pid_t實際就是int,在sys/types.h中被宏定義的

傳回值:同fork函數

函數說明:vfork函數和fork函數的作用都是建立一個新的程序,但是他們有以下幾點不同

1)vfork函數---父子程序共享代碼段和以及資料空間(資料段和堆棧段);fork函數---父子程序共用代碼段,但是資料段和堆棧段是獨立的。

2)vfork函數---保證子程序先運作,隻有子程序調用exit等函數退出程序時,父程序才能運作,否則阻塞;fork函數---不保證父子程序哪個先執行

示例代碼:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

static int s_nTmp1 = 0;
static int s_nTmp2 = 0;

int main(void)
{
	int nTmp = 0;
	pid_t pid = 0;

	printf("Before fork\n");

	pid = vfork();

	printf("This is a test\n");
	
	if(pid > 0)
	{
		printf("這是父程序,它的pid=%d,他的子程序的pid=%d\n", getpid(), pid);
		printf("父程序--nTmp = %d\n", nTmp);
		printf("父程序--s_nTmp1 = %d\n", s_nTmp1);
		printf("父程序--s_nTmp2 = %d\n", s_nTmp2);
	}	
	else
	{
		nTmp   = 1;
		s_nTmp1 = 1;
		s_nTmp2 = 1;
		printf("這是子程序,它的pid=%d,它的父程序的pid=%d\n", getpid(), getppid());
		printf("子程序--nTmp = %d\n", nTmp);
		printf("子程序--s_nTmp1 = %d\n", s_nTmp1);
		printf("子程序--s_nTmp2 = %d\n", s_nTmp2);		
		sleep(10);
		exit(0);
	}
	
	return 0;
}
           

執行結果:

Before fork

This is a test

這是子程序,它的pid=2234,它的父程序的pid=2233

子程序--nTmp = 1

子程序--s_nTmp1 = 1

子程序--s_nTmp2 = 1

This is a test

這是父程序,它的pid=2233,他的子程序的pid=2234

父程序--nTmp = 1

父程序--s_nTmp1 = 1

父程序--s_nTmp2 = 1

結果分析:

調用vfork函數肯定是先執行子程序的代碼,隻有子程序退出後,父程序才得以運作,是以在執行完

printf("子程序--s_nTmp2 = %d\n", s_nTmp2);後,子程序休眠10s,等待10s之後子程序退出,此時才執行父程序代碼。
           

執行結果隻有上面這一種。在子程序中改變了資料段和堆棧段的資料,都會影響父程序。

注意點:調用vfork函數建立子程序,要防止互鎖導緻程式無法繼續往下執行。比如

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(void)
{
	int nTmp = 0;
	pid_t pid = 0;

	printf("Before fork\n");

	pid = vfork();

	printf("This is a test\n");
	
	if(pid > 0)
	{
		nTmp = 1;
	}	
	else
	{
		while(1)
		{
			if(1 == nTmp)
			{
				printf("執行子程序\n");
				exit(0);
			}
		}
	}
	
	return 0;
}
           

這段代碼在列印完This is a test之後,将阻塞,子程序一直等待nTmp等1時才會退出,而nTmp隻在父程序中被指派為1,父程序隻有在子程序結束時才能執行,導緻了父子程序之間互鎖。

初學者筆記,若有錯誤,請指出,謝謝!

繼續閱讀