天天看點

1.2 fork()&vfork()函數1.fork()簡介2.fork()使用3.vfork()

  你将了解到以下内容:

  fork()函數功能

  vfork()函數功能

  fork()&vfork()差別比較

/-----------------------------------------------------------------------------------------------------------------------------------------/

1.fork()簡介

#include <unistd.h>
pid_t fork(void);   //傳回值:子程序中傳回0,父程序傳回子程序ID,出錯傳回-1
           

  fork英文譯“叉"、”分支“,專業領域又名"複刻",從其翻譯不難猜出它的大概作用。fork()函數通過系統調用建立一個與原來程序幾乎完全相同的程序,也就是兩個程序可以做完全相同的事,但如果初始參數或者傳入的變量不同,兩個程序也可以做不同的事。

  建立新的子程序後,兩個程序将執行fork()系統調用之後的下一條指令。子程序使用相同的pc(程式計數器),相同的CPU寄存器,在父程序中使用的相同打開檔案。

  fork函數被調用一次但傳回兩次。兩次傳回的唯一差別是子程序中傳回0值而父程序中傳回子程序ID(注意上面說的執行完fork後,父、子程序都跑起來)。我們用一下例子簡單驗證其傳回值。

2.fork()使用

#include <unistd.h>
#include <stdio.h>
/*
 * pid_t fork();
 * return :in parent process :child process ID 
 *         in child process  :0
 *         if error          :return < 0 
 */
int main(void)
{
	pid_t fpid;   //變量類型根據fork傳回值類型
	int count = 0;

	if ((fpid = fork()) < 0)
		printf("sorry error fork\n");
	else if (fpid == 0)
	{
		printf("I am the child process,my process id is %d\n", getpid());
		printf("我是子程序\n");
		count++;
	}
	else
	{
		printf("I am the parent process, my process id is %d\n", getpid());
		printf("我是父程序\n");
		count++;
	}
	printf("統計結果:count:%d\n",count);
	return 0;
}
           

運作結果是:

I am the parent process, my process id is 24440

我是父程序

統計結果:count:1

I am the child process,my process id is 24441

我是子程序

統計結果:count:1

  可以了解到兩個程序都會跑下去(從第一個if語句開始),而且父、子程序跑到代碼段是一樣的,隻不過因為(fork()兩次傳回很有意思哈)傳回值不同,是以其列印處理不一樣罷了。代碼中特意加入了count變量來計數,為的是驗證子程序對變量的修改是否影響父程序的變量。顯而易見沒有改變——count 都是1。

  關于fork()兩次傳回值不同的問題,在檢視網上的資料時看到一個另一番風味的解釋。

引用一位網友的話來解釋fpid的值為什麼在父子程序中不同。“其實就相當于連結清單,程序形成了連結清單,父程序的fpid(p 意味point)指向子程序的程序id, 因為子程序沒有子程序,是以其fpid為0

摘自《https://www.cnblogs.com/dongguolei/p/8086346.html》

   fork出錯可能有兩種原因:

  1)目前的程序數已經達到了系統規定的上限,這時errno的值被設定為EAGAIN。

  2)系統記憶體不足,這時errno的值被設定為ENOMEM。

  建立新程序成功後,系統中出現兩個基本完全相同的程序,這兩個程序執行沒有固定的先後順序,哪個程序先執行要看系統的程序排程政策。這也就導緻的放你使用不同的系統一樣的運作這個程式,會出現父子程序的列印先後不一樣的情況發生。

  我們不妨使用下面的程式來加深對fork和pid的印象。

#include <unistd.h>
#include <stdio.h>
/* 加深對程序建立的印象 */
int main()
{
	int i = 0;
	printf("first process id = %d \n", getpid()); //(1)
	printf("num ppid  pid   child\n");   //(2)
	for (i = 0; i < 2; i++)
	{
		pid_t fpid = fork(); //(3)
		if (fpid == 0)
			printf("%d   %d %d %d   child\n", i, getppid(), getpid(), fpid);
		else if (fpid > 0)
			printf("%d   %d %d %d   parent\n", i, getppid(), getpid(), fpid);
		else
			printf("                  error\n");
		sleep(2);
	}
	return 0;
}
           

以下為編譯後的列印内容:

first process id = 42624

num ppid pid child

0 21071 42624 42625 parent

0 42624 42625 0 child

1 21071 42624 42626 parent

1 42624 42625 42627 parent

1 42625 42627 0 child

1 42624 42626 0 child

  咋一看有點懵是嗎?沒關系我們把資料編輯下成下面的樣式。

num ppid pid child 123
21071 42624 42625 parent
42624 42625 child
1 21071 42624 42626 parent
1 42624 42625 42627 parent
1 42625 42627 child
1 42624 42626 child

  通過以上表格的标紅資料,可以串一個連結清單來表示其父子關系:21071->42624->42625->42627。我們來分析其實作過程:

  程式開始之初為21071程序,程式走至(3)處建立42624子程序,下一條語句開始就是兩個程序在跑了;根據fork的傳回值列印相關語句,代碼中sleep(2)是為了裝置上列印的可讀性設定的,讀者可根據自己運作結果删除, 而且我們不能保證父子程式誰先誰後跑完這裡!。休眠完之後,i++,此時i = 1,到目前為止,我們得到了兩條程序,并且已經進行了兩次列印了。如下程式:

21071父程序列印:

for (i = 0; i < 2; i++)
{
	pid_t fpid = fork(); //20171程序建立子程序42624,
	if (fpid == 0)
		printf("%d   %d %d %d       child\n", i, getppid(), getpid(), fpid);
	else if (fpid > 0)   //**父程序進行列印**
		printf("%d   %d %d %d   parent\n", i, getppid(), getpid(), fpid);
	else
		printf("                  error\n");
	sleep(2);
}
           

42624子程序列印:

for (i = 0; i < 2; i++)
{
	pid_t fpid = fork(); //20171程序建立子程序42624,
	if (fpid == 0)		 //**子程序進行列印**
		printf("%d   %d %d %d       child\n", i, getppid(), getpid(), fpid);
	else if (fpid > 0)   
		printf("%d   %d %d %d   parent\n", i, getppid(), getpid(), fpid);
	else
		printf("                  error\n");
	sleep(2);
}
           

  是以可以看到我們num=0的兩條列印。請留意現在我們已經有兩條程序了,父:21071,子:42624。

  接下來i = 1的情況下進行第二次循環了。其實這時候兩個程序都會建立新的程序,是以有原來的兩個程序分别fork,我們會得到新的兩個程序42626、42627,接下來下面的列印語句就會有四個程序執行列印,我們會得到新的四條列印。同樣用連結清單的方式來辨別程序的發展。

  21071->42624->42625->42627。

  21071->42624->42626。

  讀者可能看到這裡會有點蒙怎麼突然多了兩條列印啊?ԅ(¯㉨¯ԅ),額,我們注意一個點,列印是在建立完子程序後。兩個子程序建立完成後就是四條程序往下跑了。

  總結一下,fork()函數有兩個基本知識點是我們需要注意:

  (1)fork()的功能是怎樣?系統調用建立一個子程序。什麼樣的子程序?“複制品”!

  (2)fork()的傳回值是哪幾個?

    1)在父程序中,fork傳回新建立子程序的程序ID;

    2)在子程序中,fork傳回0;

    3)如果出現錯誤,fork傳回一個負值;

3.vfork()

  一件事物的存在總會有其可取之處,而vfork()作為fork()的一個變種,就與後者有許多不同之處。

  (1)fork():子程序拷貝父程序的資料段,代碼段。vfork():子程序與父程序共享資料段。

  (2)fork()父子程序的執行次序不确定。vfork 保證子程序先運作,在調用exec 或exit 之前與父程序資料是共享的,在它調用exec或exit 之後父程序才可能被排程運作。

  (3)vfork()保證子程序先運作,在她調用exec 或exit 之後父程序才可能被排程運作。如果在調用這兩個函數之前子程序依賴于父程序的進一步動作,則會導緻死鎖。

  回看上文中的第一個例子,其列印是:

I am the parent process, my process id is 24440

我是父程序

統計結果:count:1

I am the child process,my process id is 24441

我是子程序

統計結果:count:1

  我們特意強調過“count = 1“的問題,因為父子程序變量是不互相影響的,它們的實行順序也是不定的。我們把程式中的fork換成vfork試試:

#include <unistd.h>
#include <stdio.h>
/*
 * pid_t fork();
 * return :in parent process :child process ID 
 *         in child process  :0
 *         if error          :return < 0 
 */
int main(void)
{
	pid_t fpid;   //變量類型根據fork傳回值類型
	int count = 0;

	if ((fpid = vfork()) < 0)    //vfork
		printf("sorry error fork\n");
	else if (fpid == 0)
	{
		printf("I am the child process,my process id is %d\n", getpid());
		printf("我是子程序\n");
		count++;
	}
	else
	{
		printf("I am the parent process, my process id is %d\n", getpid());
		printf("我是父程序\n");
		count++;
	}
	printf("統計結果:count:%d\n",count);
	return 0;
}
           

I am the child process,my process id is 47043

我是子程序

統計結果:count:1

I am the parent process, my process id is 47042

我是父程序

統計結果:count:-1578414010

a.out: cxa_atexit.c: 100: __new_exitfn: Assertion `l != NULL’ failed.Aborted (core dumped)

  不負所望地出錯了,為什麼會出現這個情況?本來父子程序共享資料段,count應該為2,但是卻錯誤退出了。不妨仔細看看兩者差別的第三點,vfork()保證子程序先運作,在她調用exec 或exit 之後父程序才可能被排程運作。如果在調用這兩個函數之前子程序依賴于父程序的進一步動作,則會導緻死鎖。加入将子程序傳回if中修改如下:

else if (fpid == 0)
	{
		printf("I am the child process,my process id is %d\n", getpid());
		printf("我是子程序\n");
		count++;
		_exit(0);
	}
           

修正代碼的列印為:

I am the child process,my process id is 47080

我是子程序

I am the parent process, my process id is 47079

我是父程序

統計結果:count:2

  不光count = 2驗證了vfork建立的子程序與父程序共享資料,也驗證了子程序先運作後,再進行父程序的執行。

  為什麼會有vfork,因為以前的fork 很傻, 它建立一個子程序時,将會建立一個新的位址空間,并且拷貝父程序的資源,而往往在子程序中會執行exec 調用,這樣,前面的拷貝工作就是白費力氣了,這種情況下,聰明的人就想出了vfork,它産生的子程序剛開始暫時與父程序共享位址空間(其實就是線程的概念了),因為這時候子程序在父程序的位址空間中運作,是以子程序不能進行寫操作,并且在兒子 霸占”着老子的房子時候,要委屈老子一下了,讓他在外面歇着(阻塞),一旦兒子執行了exec 或者exit 後,相 于兒子買了自己的房子了,這時候就相 于分家了。

https://blog.csdn.net/jianchi88/article/details/6985326