fork和vfork,return和exit的理解

fork和vfork的差異:linux

一、fork是建立一個子進程,並把父進程的內存數據copy到子進程中。c++

vfork是建立一個子進程,並和父進程的內存數據share一塊兒。ide

二、vfork是這樣的工做的:函數

(1)、保證子進程先執行。優化

(2)、當子進程調用exit()或exec()後,父進程往下執行。spa

三、fork後來採用的優化技術,這樣,對於fork後並非立刻拷貝內存,而是隻有你在須要改變的時候,纔會從父進程中拷貝到子進程中,這樣fork後立馬執行exec的成本就很是小了。而vfork由於共享內存因此比較危險。操作系統

 

爲何會出現vfork線程

緣由是: 起初只有fork,可是不少程序在fork一個子進程後就exec一個外部程序,因而fork須要copy父進程的數據這個動做就變得毫無心了,並且還很重,因此,搞出了個父子進程共享的vfork。因此,vfork本就是爲了exec而生。指針

exit與return的區別:對象

exit函數在頭文件stdlib.h中。

exit(0):正常運行程序並退出程序;

exit(1):非正常運行致使退出程序;

return():返回函數,若在main主函數中,則會退出函數並返回一值,能夠寫爲return(0),或return 0

詳細說:

1. return返回函數值,是關鍵字;exit是一個函數。

2. return是語言級別的,它表示了調用堆棧的返回;而exit是系統調用級別的,它表示了一個進程的結束。
3. return是函數的退出(返回);exit是進程的退出。

4. return是C語言提供的,exit是操做系統提供的(或者函數庫中給出的)。

5. return用於結束一個函數的執行,將函數的執行信息傳出個其餘調用函數使用;exit函數是退出應用程序,刪除進程使用的內存空間,並將應用程序的一 個狀態返回給OS,這個狀態標識了應用程序的一些運行信息,這個信息和機器和操做系統有關,通常是 0 爲正常退出,非0 爲非正常退出。

6. 非主函數中調用returnexit效果很明顯,可是在main函數中調用returnexit的現象就很模糊,多數狀況下現象都是一致的。

 

爲何return會掛掉,exit()不會?

從上面咱們知道,結束子進程的調用是exit()而不是return,若是你在vfork中return了,那麼,這就意味main()函數return了,注意由於函數棧父子進程共享,因此整個程序的棧就出現問題了。若是你在子進程中return,那麼基本是下面的過程:

(1)、首先子進程的main() 函數 return了。

(2)、而main()函數return後,一般會調用 exit()或類似的函數(如:exitgroup())。

(3)、這時,父進程收到子進程exit(),開始從vfork返回,可是父進程的棧都被子進程幹廢掉了,你讓我怎麼執行?(注:棧會返回一個詭異一個棧地址,對於某些內核版本的實現,直接報「棧錯誤」,然而,對於某些內核版本的實現,因而有可能會再次調用main(),因而進入了一個無限循環的結果,直到vfork 調用返回 error)

再回到 return 和 exit,return會釋放局部變量,並彈棧,回到上級函數執行。exit直接退掉。若是你用c++ 你就知道,return會調用局部對象的析構函數,exit不會。(注:exit不是系統調用,是glibc對系統調用 _exit()或_exitgroup()的封裝)。

可見,子進程調用exit() 沒有修改函數棧,因此,父進程得以順利執行。

 

內核代碼分析:

linux建立子進程實際是一個複製父進程的過程。因此更貼切的說法是clone。linux一開始使用fork的緣由是當時clone這個詞尚未流行。 實際存在fork,clone,vfork 三個系統調用。fork是徹底複製,clone則是有選擇的複製,vfork則徹底使用父進程的資源。能夠理解vfork是建立的線程。 vfork的出現主要是爲了當即就執行exec的程序考慮的。可是後來的kernel都支持copy_on_write ,因此vfork提升效率的機制也沒有那麼明顯了。

內核中三個系統調用最後都是調用do_fork:

fork:return do_fork(SIGCHLD, regs.esp, s, 0);

clone:return do_fork(clone_flags, newsp, s, 0);

vfork: return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, s, 0);

 

#define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release*/

#define CLONE_VM 0x00000100  /* set if VM shared between processes */

上面兩個宏指出:vfork 要求子進程執行mm_release 後喚醒 父進程, 而且共享虛擬內存

 

爲何要求子進程先行呢?

拿虛擬內存作比方。 進程須要有結構管理本身的虛擬內存空間, 該結構在進程 結構體 task_struct 中就是一個mm_struct 類型的指針。fork的時候內核會新建結構體,將該mm_struct 自己以及下級結構都複製一份,並設置子進程的mm_struct 指向新的內存。而vfork則只是複製了task_struct 自己,並無遞歸下去。簡單說就是:fork複製了內存,vfork複製了指針。

 

相關例子:

fork

wKiom1dipdCCezOvAABOLcIcNeA694.png-wh_50

 vfork

wKioL1dipxyzf6EkAABU_V_8Bgw058.png-wh_50

運行結果爲:

fork運行結果:

wKioL1dip5SwoACRAAAXSuAu6yY596.png-wh_50

vfork運行結果:

一、return返回時出錯:

wKioL1dip1LBNVCLAAA0T-vogDc379.png-wh_50

二、exit返回結果:

wKiom1diplaC4HxLAAAVaPSUz7k360.png-wh_50


運行結果說明:vfrok時父、子進程共享數據段,fork時是進行拷貝。若是,vfork子進程中,使用return返回時,出現段錯誤。

相關文章
相關標籤/搜索