clone和fork的区别linux,Linux-fork(),vfork()和clone的區別

在linux系統中,fork(),vfork()和clone函數都可以創建一個進程,但是它們的區別是什么呢???本文就這三者做一個較深入的分析!!!

1.fork()

fork()函數的作用是創建一個新進程,由fork創建的進程稱為子進程,fork函數調用一次返回兩次,子進程返回值為0,父進程返回子進程的進程ID。我們知道,一個進程的地

址空間主要由代碼段,數據段,堆和棧構成,那么p2就要復制相關的段到物理內存。原始的unix系統的實現的是一種傻

瓜式的進程創建,這些復制包括:

(1)  為子進程的頁表分配頁面,確定頁表的位置;

(2)為子進程的頁分配頁面,確定子進程頁面的位置;

(3)初始化子進程的頁表;

(4)把父進程的頁復制到子進程對應的頁中

從圖中我們可以看出除了正文段外,子進程的所有其它段都分配了物理空間,並將父進程的相關內容拷貝過來。父進程的task_struct結構中的打開文件描述符,進程組ID,

回話ID都進行復制。

03c10f0dbef80772c860eb7b623e1df5.png

下面通過簡單的代碼檢測一下fork()函數:

#include

#include

#include

#include

#include

#include

#include

using namespace std;

int main() {

int num = 1;

int child;

if(!(child =fork())) {

cout<

printf("son process, num: %d, address:%p, pid is: %d\n", num,&num, getpid());

num++;

cout<

cout<

sleep(60);

exit(0);

} else {

sleep(1);

printf("father process, num: %d, address:%p, pid is: %d\n", num,&num, getpid());

sleep(59);

exit(0);

}

}

a601ec5c70327313bc65c379e164e03f.png

進程的虛擬空間:

子進程的虛擬地址空間:

0c6e15d5e5286bd2297c7811d7be7a40.png

父進程的虛擬地址空間:

67d1060aca60468b87ad28af3e0a7d8a.png

可以看到子進程和父進程的地址空間是一樣的。

但是這種方法的效果非常不好,如果在fork子進程之后,立即調用了exec函數簇,那么原先拷貝的父進程的數據段,棧,堆的相關副本都將變為徒勞。后來人們想到了一種

替代的方式,那就是寫時復制(Copy-On-write,COW技術),這種技術允許父進程和子進程共享上面的區域,而且內核將這些段的訪問權限變成只讀。如果是父進程或是子進程中

的任何一個試圖修改這些區域,則內核只為修改區域的那塊內存制作一個副本,通常是虛擬存儲系統的某一個"頁".下面是這種技術在沒有寫操作的時候的的詳細圖解,如果子進

程或父進程的任何一個有寫操作的話,那么被寫的那一頁才需要復制到物理空間;

d04c031de39209a9e2a86531fd21eab6.png

從圖中可以看到,COW技術在進程調用fork()的時候,並沒給子進程的相關段分配內存空間。這種做法在fork()之后調用exec函數簇的過程中效率有很大的提高。這種情況下,內

核只需要為子進程創建一個task_struct,也就是說如果子進程調用exec函數簇執行了另一個可執行文件,那么內核可能只是新建一個task_struct結構體,將父進程的內容拷貝過

,這樣就大大節約了盲目拷貝的消耗。

2.vfork()

vfork()函數也用於創建一個新進程,而該新進程的目的是exec一個新程序,下面我們開看看簡單的測試:

#include

#include

#include

#include

#include

#include

#include

using namespace std;

int main() {

int num = 1;

int child;

if(!(child =vfork())) {

cout<

printf("son process, num: %d, address:%p, pid is: %d\n", num,&num, getpid());

num++;

cout<

cout<

sleep(2);

exit(0);

} else {

sleep(1);

printf("father process, num: %d, address:%p, pid is: %d\n", num,&num, getpid());

sleep(2);

exit(0);

}

}

測試結果:

1014a62a8555017a323f3bdd0748cae9.png

從測試結果中我們可以看到,在子進程修改了num變量的值后,父進程的num的值也發生改變,說明對於子進程和父進程來說,它們操作的是同一個地方的num值,下面就是vfork的示意圖:

58d491402b37ef29cccf5f69eeb0ea44.png

可以看出子進程直接共享了父進程的虛擬進程空間。

3.clone()

clone()函數是linux系統中,用來創建輕量級進程。

函數原形:

int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);

下面是flags可以取的值

標志 含義

CLONE_PARENT 創建的子進程的父進程是調用者的父進程,新進程與創建它的進程成了“兄弟”而不是“父子”

CLONE_FS 子進程與父進程共享相同的文件系統,包括root、當前目錄、umask

CLONE_FILES 子進程與父進程共享相同的文件描述符(file descriptor)表

CLONE_NEWNS 在新的namespace啟動子進程,namespace描述了進程的文件hierarchy

CLONE_SIGHAND 子進程與父進程共享相同的信號處理(signal handler)表

CLONE_PTRACE 若父進程被trace,子進程也被trace

CLONE_VFORK 父進程被掛起,直至子進程釋放虛擬內存資源

CLONE_VM 子進程與父進程運行於相同的內存空間

CLONE_PID 子進程在創建時PID與父進程一致

CLONE_THREAD Linux 2.4中增加以支持POSIX線程標准,子進程與父進程共享相同的線程群

下面的例子是創建一個線程(子進程共享了父進程虛存空間,沒有自己獨立的虛存空間不能稱其為進程)。父進程被掛起當子線程釋放虛

存資源后再繼續執行。

測試代碼1:

#include

#include

#include

#include

#include

#include

#include

using namespace std;

#define FIBER_STACK 8192

int a;

void * stack;

int func(void *){

cout<

printf("This is son, the pid is:%d, the a is: %d\n", getpid(), ++a);

free(stack);

exit(1);

}

int main(){

void * stack;

a = 1;

stack = malloc(FIBER_STACK);

if(!stack) {

printf("The stack failed\n");

exit(0);

}

cout<

printf("creating son thread!!!\n");

clone(func, (char *)stack + FIBER_STACK,CLONE_VFORK|CLONE_VM, 0);

printf("This is father, my pid is: %d, the a is: %d\n", getpid(), a);

exit(1);

}

結果:

21d004b2e9fa756b9c0ac3a94924ba5b.png

測試代碼2(做如下修改):

clone(func, (char *)stack + FIBER_STACK,CLONE_VFORK, 0);結果:

c2d14899f6011efc8936286ae88f5042.png

很明顯,在測試2中將CLONE_VM刪掉之后,子進程和父進程就不會公用頁表,子進程創建新的頁表。從某種意義上來說,clone其實是fork和vfrok的更高層次版本,,它們的關

系如下(《深入理解linux內核》中描述):

傳統的fork()系統調用在Linux中是用clone()實現的,其中clone()的flags參數指定為sigchld信號以及所有清0的clone標志,而它的child_stack參數是父進程當前的堆棧

指針,因此,父進程和子進程暫時共享一個用戶態堆棧。而vfork函數系統調用也是用clone實現的,其中clone()的參數flags指定為sigchld和CLONE_VFORK和CLONE_VM標

志,clone()的參數child_stack等於父進程當前的棧指針!!!

。只是有一點不明白,把int a和void * stack挪到main函數里面之后,就會出現編譯錯誤,顯示未定義a和stack,這點有些不懂,望高人指點!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值