淺談Linux vfork與fork簡單對比分析
本文分享了Linux vfork與fork簡單對比分析,分享給大家,具體如下:
fork相關(guān)問題:
一、fork基礎了解
fork作用為創(chuàng)建一個子進程,在使用了fork命令后,內(nèi)核會分配新的內(nèi)存塊和數(shù)據(jù)結(jié)構(gòu)給子進程,并且將父進程的部分數(shù)據(jù)結(jié)構(gòu)內(nèi)容拷貝到子進程,最后再將子進程添加到系統(tǒng)進程列表中,添加完成后fork返回,開始調(diào)度。
頭文件:#include < unistd.h >
函數(shù)原型:pid_t fork( )
返回值:返回值大于0則當前進程為父進程,等于0代表為子進程,小于零代表創(chuàng)建子進程失敗。
通過一個例子來了解:
#include <stdio.h> #include <unistd.h> int main() { int tmp = 5; pid_t res = fork(); if(res < 0){ //fork失敗 perror("fork"); }else if(res == 0){ //該進程為子進程 printf("im child[%d],fasther is %d,tmp is %d.\n",getpid(),getppid(),tmp++); }else{ //該進程為父進程 printf("im father[%d],tmp is %d.\n",getpid(),tmp++); } printf("tmp = %d\n",tmp); return 0; }
運行結(jié)果:
im father[3128],tmp is 5.
tmp = 6
im child[3129],fasther is 1,tmp is 5.
tmp = 6
相關(guān)問題小結(jié):
通過結(jié)果很明顯的能看出本次調(diào)用中,先執(zhí)行父進程,對應pid為3128,在父進程中tmp++,所以輸出為6;關(guān)鍵問題在于子進程,有兩個關(guān)鍵點。
①為什么結(jié)果中子進程父親pid為1:通過輸出我們能看出父進程先執(zhí)行完成后才執(zhí)行的子進程,也就是說當子進程執(zhí)行時父進程已結(jié)束,此時該子進程相當于一個孤兒進程,被pid為1也就是Init進程所管理,所以子進程的ppid為1;
②為什么子進程最后輸出tmp值還為6: fork進程采用的是寫時拷貝,父子進程一開始共享一片內(nèi)存區(qū)域,但是只有有一方要對數(shù)據(jù)進行修改,則再開辟一塊空間,防止相互修改影響。所以在上述代碼中,雖說是一個tmp,其實內(nèi)存中各自保留了一份值。
二、關(guān)于fork過程中寫時拷貝:
這下就不難看出,父子進程數(shù)據(jù)段和代碼段開始時是共享一塊對應的內(nèi)存,當一方嘗試寫入時,便產(chǎn)生了寫時拷貝。需要注意的是:fork之前,父進程獨立執(zhí)行,fork之后,父子兩個執(zhí)行流分別執(zhí)行,至于誰先執(zhí)行,由調(diào)度器決定??赏ㄟ^下面例子很明顯的看出是從fork之后才分別執(zhí)行。
#include <stdio.h> #include <unistd.h> int main() { int tmp = 5; printf("There is fork before\n"); pid_t res = fork(); if(res < 0){ //fork失敗 perror("fork"); }else if(res == 0){ //該進程為子進程 printf("im child[%d],tmp is %d.\n",getpid(),tmp++); }else{ //該進程為父進程 printf("im father[%d],tmp is %d.\n",getpid(),tmp++); } printf("tmp = %d\n",tmp); return 0; }
輸出結(jié)果:
There is fork before
im father[3625],tmp is 5.
tmp = 6
im child[3626],tmp is 5.
tmp = 6
三、fork調(diào)用失敗的原因:
①系統(tǒng)中已經(jīng)存在太多進程,無法再創(chuàng)建新的進程??赏ㄟ^ulimit -a命令查看當前所有的資源限制。
②內(nèi)存不足,由于開辟每個新的進程都要分配一個PCB,并為新進程分配資源,內(nèi)存都不足也就別提還想著再創(chuàng)建進程了。
vfork相關(guān)問題:
一、vfork基礎了解
<1>vfork創(chuàng)建新進程的主要目的在于用exec函數(shù)執(zhí)行另外的程序,實際上,在沒調(diào)用exec或_exit之前子進程與父進程共享數(shù)據(jù)段。在vfork調(diào)用中,子進程先運行,父進程掛起,直到子進程調(diào)用exec或_exit,在這以后,父子進程的執(zhí)行順序不再有限制。
頭文件:#include < unistd.h >
函數(shù)原型:pid_t vfork( )
返回值:返回值大于0則當前進程為父進程,等于0代表為子進程,小于零代表創(chuàng)建子進程失敗。
通過一個例子來了解:
#include <stdio.h> #include <unistd.h> int tmp = 3; int main() { pid_t res = vfork(); if(res < 0){ perror("vfork"); _exit(); }else if(res == 0){ tmp = 10; printf("child res = %d\n",tmp); _exit(0); }else{ printf("father res = %d\n",tmp); } return 0; }
輸出結(jié)果:
child res = 10
father res = 10
結(jié)果分析:正如上面所說的,子進程直接公用父進程的頁表,改變子進程的數(shù)據(jù)也會影響到父進程。
<2>vfork用處:
vfork()跟fork()類似,都是創(chuàng)建一個子進程,這兩個函數(shù)的的返回值也具有相同的含義。但是vfork()創(chuàng)建的子進程基本上只能做一件事,那就是立即調(diào)用_exit()函數(shù)或者exec函數(shù)族成員,調(diào)用任何其它函數(shù)(包括exit())、修改任何數(shù)據(jù)(除了保存vfork()返回值的那個變量)、執(zhí)行任何其它語句(包括return)都是不應該的。更需要注意的是:調(diào)用vfork()之后,父進程會一直阻塞,直到子進程調(diào)用_exit()終止,或者調(diào)用exec函數(shù)族成員。
<3>為什么只能用_exit退出:
exit()是對_exit()的封裝,它自己在調(diào)用_exit()前會做很多清理工作,其中包括刷新并關(guān)閉當前進程使用的流緩沖(比如stdio.h里面的printf等),由于vfork()的子進程完全共享了父進程地址空間,子進程里面的流也是共享的父進程的流,所以子進程里面是不能做這些事的。直接return就更不行了,子進程return以后,會從當前函數(shù)的外部調(diào)用點后面繼續(xù)執(zhí)行,這后面子進程可能將會執(zhí)行很多語句,結(jié)果就沒法預料了。在man手冊中也強調(diào)了這一點,必須使用_exit退出。
fork與vfork的區(qū)別
1.vfork保證子進程先運行,在它調(diào)用exec或exit之后父進程才可能被調(diào)度運行。如果在調(diào)用這兩個函數(shù)之前子進程依賴于父進程的進一步動作,則會導致死鎖。
2.fork要拷貝父進程的進程環(huán)境;而vfork則不需要完全拷貝父進程的進程環(huán)境,在子進程沒有調(diào)用exec和exit之前,子進程與父進程共享進程環(huán)境,相當于線程的概念,此時父進程阻塞等待。
為什么會有vfork呢?
因為以前的fork當它創(chuàng)建一個子進程時,將會創(chuàng)建一個新的地址空間,并且拷貝父進程的資源,然后將會有兩種行為:
1.執(zhí)行從父進程那里拷貝過來的代碼段
2.調(diào)用一個exec執(zhí)行一個新的代碼段
當進程調(diào)用exec函數(shù)時,一個新程序替換了當前進程的正文,數(shù)據(jù),堆和棧段。這樣,前面的拷貝工作就是白費力氣了,這種情況下,聰明的人就想出了vfork。vfork并不復制父進程的進程環(huán)境,子進程在父進程的地址空間中運行,所以子進程不能進行寫操作,并且在兒子“霸占”著老子的房子時候,要委屈老子一下了,讓他在外面歇著(阻塞),一旦兒子執(zhí)行了exec或者exit后,相當于兒子買了自己的房子了,這時候就相當于分家了。
因此,如果創(chuàng)建子進程是為了調(diào)用exec執(zhí)行一個新的程序的時候,就應該使用vfork
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Linux 基于CentOS的LNMP 服務器部署標準 新手簡明版
Linux 基于CentOS的LNMP 服務器部署標準 新手簡明版,需要配置centos服務器的朋友可以參考下。2011-01-01Linux 新的API signalfd、timerfd、eventfd使用說明
這篇文章主要介紹了Linux 新的API signalfd、timerfd、eventfd使用說明的相關(guān)資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-10-10關(guān)于安裝linux redhat后無法使用yum命令安裝gcc-c++問題的解決過程
這篇文章主要介紹了關(guān)于安裝linux redhat后無法使用yum命令安裝gcc-c++問題的解決過程,需要的朋友可以參考下2017-08-08