Posted on 2012-10-15 11:18
鑫龍 閱讀(202)
評論(0) 編輯 收藏 引用 所屬分類:
linux內核
今天看到寫時拷貝這個概念,當時一下沒有理解,后來查看一些網上的資料,找到了這篇文章,里面的那份個小程序能夠很好的說明進程創建寫時拷貝的概念。怕以后找不到就轉載了。嘿嘿。下面是那篇文章的原文: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 %dn",getpid(),data); data = 20; printf("Child process %d, data %dn",getpid(),data); while(1);}int main(int argc, char* argv[]){ if(fork()==0) { child_process(); }else{ sleep(1); printf("Parent process %d, data %dn",getpid(), data); while(1); }}運行結果Child process 6427, data 10Child process 6427, data 20Parent 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 <stdio.h> #include <sched.h> #include <stdlib.h>
int data = 10;
int child_process() { printf("Child process %d, data %dn",getpid(),data); data = 20; printf("Child process %d, data %dn",getpid(),data); while(1); }
int main(int argc, char* argv[]) { 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 %dn",getpid(), data); while(1); }
|
運行結果將是Child process 6443, data 10 Child process 6443, data 20 Parent process 6442, data 20
|
由于使用了CLONE_VM創建進程,子進程的mm實際直接指向父進程的mm,所以data是同一份。改變父子進程的data都會互相看到。