1. 程式人生 > >【轉】Copy-On-Write技術 [ linux fork程序時使用技術]

【轉】Copy-On-Write技術 [ linux fork程序時使用技術]

inux核心在使用fork建立程序時,基本上會使用Copy-On-Write(COW)技術。這裡解釋一下COW技術以及為什麼在fork中使用。

WIKI上對COW的解釋:

Copy-on-write (sometimes referred to as "COW") is an optimization strategy used in computer programming. The fundamental idea is that if multiple callers ask for resources which are initially indistinguishable, they can all be given pointers to the same resource. This function can be maintained until a caller tries to modify its "copy" of the resource, at which point a true private copy is created to prevent the changes becoming visible to everyone else. All of this happens 

transparently to the callers. The primary advantage is that if a caller never makes any modifications, no private copy need ever be created.

意思上就是:在複製一個物件的時候並不是真正的把原先的物件複製到記憶體的另外一個位置上,而是在新物件的記憶體對映表中設定一個指標,指向源物件的位置,並把那塊記憶體的Copy-On-Write位設定為1.

這樣,在對新的物件執行讀操作的時候,記憶體資料不發生任何變動,直接執行讀操作;而在對新的物件執行寫操作時,將真正的物件複製到新的記憶體地址中,並修改新物件的記憶體對映表指向這個新的位置,並在新的記憶體位置上執行寫操作。

這個技術需要跟虛擬記憶體和分頁同時使用,好處就是在執行復制操作時因為不是真正的記憶體複製,而只是建立了一個指標,因而大大提高效率。但這不是一直成立的,如果在複製新物件之後,大部分物件都還需要繼續進行寫操作會產生大量的分頁錯誤,得不償失。所以COW高效的情況只是在複製新物件之後,在一小部分的記憶體分頁上進行寫操作。

COW在程式設計中被廣泛應用。

特別是在作業系統當中,當一個程式執行結束時,作業系統並不會急著把其清除出記憶體,原因是有可能程式還會馬上再執行一次(從磁碟把程式裝入到記憶體是個很慢的過程),而只有當記憶體不夠用了,才會把這些還駐留記憶體的程式清出。

而對於Linux核心空間建立程序時的fork,由於在核心空間已經由程式碼決定不使用COW技術(參見mm/memory.c Line 221)。從而由核心空間

的程序0(main)建立程序1(init)不使用COW,系統對此次新程序建立進行了特殊處理(存在疑問,同樣是fork,如何實現對這個fork的特殊處理,估計是schedule,看到再解決了)。程序0和程序1同時使用著核心程式碼區內(<=1M)相同的程式碼和資料記憶體頁面(640KB),只是執行程式碼不在一處,因此他們也同時使用著相同的使用者堆疊區。在為程序1(init)複製其父程序(程序0)的頁目錄和頁表項時,程序0的640KB頁表項的屬性沒有改動過(仍然可讀寫),但是程序1的640KB對應的頁表項卻被設定成只讀。因此當程序1(init)開始執行時,對使用者堆疊的入棧操作將導致頁面防寫異常,從而使得核心的記憶體管理程式為程序1在主記憶體區中分配一記憶體頁面,並把程序0中的頁面內容複製到新的頁面上。從此時開始,程序1開始有自己獨立的記憶體頁面,由於此時的記憶體頁面在主記憶體區,因此程序1中繼續建立新的子程序時可以採用COW技術。

在Linux核心首先通過move_to_user_mode轉移到使用者模式下執行,至此main函式就以程序0的身份執行。而程序0是所有將建立程序的父程序,他建立程序1(init)時,fork的結果就是程序1與程序0擁有完全相同的記憶體空間、堆疊,這時程序0和程序1的記憶體還都在Linux核心空間中。

核心排程程序執行時次序是隨機的,有可能在程序0建立了進城1之後仍然允許程序0,由於兩個程序共享記憶體空間,為了不出現衝突問題,就必須要求程序0在程序1執行堆疊操作(程序1的堆疊操作會導致頁面保護異常,從而使得程序1在主記憶體區得到新的使用者頁面區,此時程序1和程序0才算是真正獨立,如前面所述)之前禁止使用使用者堆疊區。所以程序0在執行了fork(建立了程序1)之後的pause使用內嵌的方式,保證程序0(main)不會弄亂堆疊。

程序1中如果執行fork以及exec,此時的頁面空間已經到了主記憶體區,就可以使用COW了。