天天看點

Linux程序建立,子程序對父程序資源“寫時拷貝”的證明

傳統的fork()系統調用直接把所有的資源複制給新建立的程序。這種實作過于簡單并且效率低下,因為它拷貝的資料或許可以共享(This approach is significantly naïve and inefficient in that it copies much data that might otherwise be shared.)。更糟糕的是,如果新程序打算立即執行一個新的映像,那麼所有的拷貝都将前功盡棄。

Linux的fork()使用寫時拷貝 (copy- on-write)頁實作。寫時拷貝是一種可以推遲甚至避免拷貝資料的技術。核心此時并不複制整個程序的位址空間,而是讓父子程序共享同一個位址空間。隻 用在需要寫入的時候才會複制位址空間,進而使各個進行擁有各自的位址空間。也就是說,資源的複制是在需要寫入的時候才會進行,在此之前,隻有以隻讀方式共 享。這種技術使位址空間上的頁的拷貝被推遲到實際發生寫入的時候。在頁根本不會被寫入的情況下---例如,fork()後立即執行exec(),位址空間 就無需被複制了。fork()的實際開銷就是複制父程序的頁表以及給子程序建立一個程序描述符。下列程式可證明寫時拷貝:

#include <stdio.h>

#include <sched.h>

int data = 10;

int child_process()

{

    printf("Child process %d, data %d\n",getpid(),data);

    data = 20;

    while(1);

}

int main(int argc, char* argv[])

    if(fork()==0) {

child_process();    

    }

    else{

        sleep(1);

        printf("Parent process %d, data %d\n",getpid(), data);

        while(1);

運作結果

Child process 6427, data 10

Child process 6427, data 20

Parent process 6426, data 10

第 1個Child process 6427, data 10是因為子程序建立時task_struct的mm直接拷貝自parent的mm;第2個Child process 6427, data 20是因為子程序進行了“寫時拷貝”,有了自己的dataa;第3個Parent process 6426, data 10輸出10是因為子程序的data和父程序的data不是同一份。

如果把上述程式改為:

#include <stdlib.h>

    void **child_stack;

    child_stack = (void **) malloc(16384);

    clone(child_process, child_stack, CLONE_VM|CLONE_FILES|CLONE_SIGHAND, NULL);

    sleep(1);

    printf("Parent process %d, data %d\n",getpid(), data);

運作結果将是

Child process 6443, data 10

Child process 6443, data 20

Parent process 6442, data 20

由于使用了CLONE_VM建立程序,子程序的mm實際直接指向父程序的mm,是以data是同一份。改變父子程序的data都會互相看到。

 本文轉自 21cnbao 51CTO部落格,原文連結:http://blog.51cto.com/21cnbao/129485,如需轉載請自行聯系原作者

繼續閱讀