Linux-Copy On Write写时复制机制初探
文章目錄
- 生猛干貨
- COW概述
- *Unix
- fork
- 函數(shù)族exec( )
- 為什么有了COW?
- COW 原理
- COW的優(yōu)缺點
- 小結(jié)
- 搞定Linux核心技術(shù)
生猛干貨
從系統(tǒng)安裝到程序員必備的Linux技能,還原真實工作場景,手把手帶你實戰(zhàn)演練
COW概述
來看下 https://en.wikipedia.org/wiki/Copy-on-write的說明
Copy-on-write (COW), sometimes referred to as implicit sharing[1] or shadowing,[2] is a resource-management technique used in computer programming to efficiently implement a “duplicate” or “copy” operation on modifiable resources.[3] If a resource is duplicated but not modified, it is not necessary to create a new resource; the resource can be shared between the copy and the original. Modifications must still create a copy, hence the technique: the copy operation is deferred to the first write. By sharing resources in this way, it is possible to significantly reduce the resource consumption of unmodified copies, while adding a small overhead to resource-modifying operations.
寫入時復(fù)制(COW),有時也稱為隱式共享,是一種計算機管理中用來有效地對可修改資源執(zhí)行“復(fù)制”操作的資源管理技術(shù)。
如果資源重復(fù)但未修改,則無需創(chuàng)建新資源,資源可以在副本和原始副本之間共享。
修改仍然必須創(chuàng)建一個副本,因此使用COW,可以將復(fù)制操作推遲到第一次寫入。
通過以這種方式共享資源,可以顯著減少未修改副本的資源消耗,當(dāng)然了資源修改操作的時候也會增加少量開銷。
簡單來說 COW 寫時復(fù)制是提高資源使用效率的一種手段, 在內(nèi)存管理(進程的 fork),數(shù)據(jù)存儲( 比如 Docker 的 AUFS 文件系統(tǒng)),軟件開發(fā)(Java的Copy On Write容器)、高可用HA軟件中廣泛使用。
*Unix
在傳統(tǒng)的Unix環(huán)境下,有兩個基本的操作用于創(chuàng)建和修改進程:
-
函數(shù)fork( )用來創(chuàng)建一個新的進程,該進程幾乎是當(dāng)前進程的一個完全拷貝
-
函數(shù)族exec( )用來啟動另外的進程以取代當(dāng)前運行的進程(函數(shù)族exec( )是一組函數(shù)的統(tǒng)稱, 它包括了execl()、execlp()、execv()、execle()、execve()、execvp()等)
fork
fork是類Unix操作系統(tǒng)上創(chuàng)建進程的主要方法,fork用于創(chuàng)建子進程。
新的進程要通過老的進程復(fù)制自身得到,Linux下init進程是所有進程的父 。 Linux的進程都通過init進程或init的子進程fork(vfork)出來的
#include <unistd.h> #include <stdio.h> int main () { pid_t fpid; //fpid表示fork函數(shù)返回的值 int count=0;// 調(diào)用fork,創(chuàng)建出子進程 fpid=fork();// 所以下面的代碼有兩個進程執(zhí)行!if (fpid < 0) printf("創(chuàng)建進程失敗!/n"); else if (fpid == 0) { printf("我是子進程,由父進程fork出來/n"); count++; } else { printf("我是父進程/n"); count++; } printf("統(tǒng)計結(jié)果是: %d/n",count); return 0; }輸出結(jié)果
我是子進程,由父進程fork出來統(tǒng)計結(jié)果是: 1我是父進程統(tǒng)計結(jié)果是: 1fork 函數(shù)會有兩次返回 1) 將子進程的PID返回給父進程,2) 0返回給子進程。(如果小于0,則說明創(chuàng)建子進程失敗)。
當(dāng)前進程調(diào)用fork(),會創(chuàng)建一個跟當(dāng)前進程完全相同的子進程(除了pid),所以子進程同樣是會執(zhí)行fork()之后的代碼。
故: 父進程在執(zhí)行if代碼塊的時候,fpid變量的值是子進程的pid,子進程在執(zhí)行if代碼塊的時候,fpid變量的值是0
函數(shù)族exec( )
在Linux中要使用exec函數(shù)族。系統(tǒng)調(diào)用execve()對當(dāng)前進程進行替換,替換者為一個指定的程序,其參數(shù)包括文件名(filename)、參數(shù)列表(argv)以及環(huán)境變量(envp)。
exec函數(shù)族不止一個,但它們大致相同,在 Linux中,它們分別是:execl,execlp,execle,execv,execve和execvp。
一個進程一旦調(diào)用exec類函數(shù),它本身就"死亡"了,系統(tǒng)把代碼段替換成新的程序的代碼,廢棄原有的數(shù)據(jù)段和堆棧段,并為新程序分配新的數(shù)據(jù)段與堆棧段,唯一留下的,就是進程號,也就是說,對系統(tǒng)而言,還是同一個進程,不過已經(jīng)是另一個程序了。
為什么有了COW?
早期的 Unix 在實現(xiàn) fork 系統(tǒng)調(diào)用時,并沒有使用該技術(shù),創(chuàng)建新進程的開銷很大。
出于效率考慮,Copy On Write 技術(shù)引入到進程中,fork 之后的父進程和子進程完全共享數(shù)據(jù)段、代碼段、堆和棧等的完全副本。
Linux在使用fork()函數(shù)進程創(chuàng)建時,傳統(tǒng)fork()的做法是系統(tǒng)把所有的資源復(fù)制給新創(chuàng)建的進程,這種方式不僅單一,而且效率低下。因為所拷貝的數(shù)據(jù)或別的資源可能是可以共享的。現(xiàn)在Linux的fork()使用寫時拷貝頁來實現(xiàn)新進程的創(chuàng)建,它是一種可推遲甚至避免數(shù)據(jù)拷貝的技術(shù),剛開始時內(nèi)核并不會復(fù)制整個地址空間,而是讓父子進程共享地址空間,只有在寫時才復(fù)制地址空間,使得父子進程都擁有獨立的地址空間,即資源的復(fù)制是在只有需要寫入時才會發(fā)生,因此而稱之為Copy on Write(COW)。
在此之前都是以讀的方式去和父進程共享資源,這樣,在頁根本不會被寫入的場景下,fork()立即執(zhí)行exec(),無需對地址空間進行復(fù)制,fork()的實際開銷就是復(fù)制父進程的一個頁表和為子進程創(chuàng)建一個進程描述符,也就是說只有當(dāng)進程空間中各段的內(nèi)存內(nèi)容發(fā)生變化時,父進程才將其內(nèi)容復(fù)制一份傳給子進程,大大提高了效率。
通俗來說 fork創(chuàng)建出的子進程,與父進程共享內(nèi)存空間。 如果子進程不對內(nèi)存空間進行寫入操作的話,內(nèi)存空間中的數(shù)據(jù)并不會復(fù)制給子進程,這樣創(chuàng)建子進程的速度就很快 ,因為不用復(fù)制,直接引用父進程的物理空間 ,并且如果在fork函數(shù)返回之后,子進程第一時間exec一個新的可執(zhí)行映像,那么也不會浪費時間和內(nèi)存空間了 。
COW 原理
fork()之后,kernel把父進程中所有的內(nèi)存頁的權(quán)限都設(shè)為read-only,然后子進程的地址空間指向父進程。當(dāng)父子進程都只讀內(nèi)存時,相安無事。當(dāng)其中某個進程寫內(nèi)存時,CPU硬件檢測到內(nèi)存頁是read-only的,于是觸發(fā)頁異常中斷(page-fault),陷入kernel的一個中斷例程。中斷例程中,kernel就會把觸發(fā)的異常的頁復(fù)制一份,于是父子進程各自持有獨立的一份。
COW的優(yōu)缺點
優(yōu)點
- COW技術(shù)可減少分配和復(fù)制大量資源時帶來的瞬間延時。
- COW技術(shù)可減少不必要的資源分配。比如fork進程時,并不是所有的頁面都需要復(fù)制,父進程的代碼段和只讀數(shù)據(jù)段都不被允許修改,所以無需復(fù)制。
缺點
- 如果在fork()之后,父子進程都還需要繼續(xù)進行寫操作,那么會產(chǎn)生大量的分頁錯誤(頁異常中斷page-fault),這樣就得不償失。
小結(jié)
fork出的子進程共享父進程的物理空間,當(dāng)父子進程有內(nèi)存寫入操作時,read-only內(nèi)存頁發(fā)生中斷,將觸發(fā)的異常的內(nèi)存頁復(fù)制一份(其余的頁還是共享父進程的)。
fork出的子進程功能實現(xiàn)和父進程是一樣的。如果有需要, 會調(diào)用exec()把當(dāng)前進程映像替換成新的進程文件,完成自定義的功能。
參考:
維基百科-Copy-on-write
COW奶牛!Copy On Write機制了解一下
搞定Linux核心技術(shù)
總結(jié)
以上是生活随笔為你收集整理的Linux-Copy On Write写时复制机制初探的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis进阶-Redis持久化原理
- 下一篇: Java-COW在Java中的应用