Linux程序管理 fork 和寫時複製

2021-08-21 13:06:27 字數 2370 閱讀 4334

寫時複製技術最初產生於unix系統,用於實現一種傻瓜式的程序建立:當發出fork( )系統呼叫時,核心原樣複製父程序的整個位址空間並把複製的那乙份分配給子程序。這種行為是非常耗時的,因為它需要:

· 為子程序的頁表分配頁面

· 為子程序的頁分配頁面

· 初始化子程序的頁表

· 把父程序的頁複製到子程序相應的頁中

建立乙個位址空間的這種方法涉及許多記憶體訪問,消耗許多cpu週期,並且完全破壞了快取記憶體中的內容。在大多數情況下,這樣做常常是毫無意義的,因為許多子程序通過裝入乙個新的程式開始它們的執行,這樣就完全丟棄了所繼承的位址空間。

現在的unix核心(包括linux),採用一種更為有效的方法稱之為寫時複製(或cow)。這種思想相當簡單:父程序和子程序共享頁面而不是複製頁面。然而,只要頁面被共享,它們就不能被修改。無論父程序和子程序何時試圖寫乙個共享的頁面,就產生乙個錯誤,這時核心就把這個頁複製到乙個新的頁面中並標記為可寫。原來的頁面仍然是寫保護的:當其它程序試圖寫入時,核心檢查寫程序是否是這個頁面的唯一屬主;如果是,它把這個頁面標記為對這個程序是可寫的。

linux的fork()使用寫時複製

cow技術初窺:

在linux程式中,fork()會產生乙個和父程序完全相同的子程序,但子程序在此後多會exec系統呼叫,出於效率考慮,linux中引入了「寫時複製「技術,也就是只有程序空間的各段的內容要發生變化時,才會將父程序的內容複製乙份給子程序。

那麼子程序的物理空間沒有**,怎麼去取指令執行exec系統呼叫呢?

在fork之後exec之前兩個程序用的是相同的物理空間(記憶體區),子程序的**段、資料段、堆疊都是指向父程序的物理空間,也就是說,兩者的虛擬空間不同,但其對應的物理空間是同乙個。當父子程序中有更改相應段的行為發生時,再為子程序相應的段分配物理空間,如果不是因為exec,核心會給子程序的資料段、堆疊段分配相應的物理空間(至此兩者有各自的程序空間,互不影響),而**段繼續共享父程序的物理空間(兩者的**完全相同)。而如果是因為exec,由於兩者執行的**不同,子程序的**段也會分配單獨的物理空間。

在網上看到還有個細節問題就是,fork之後核心會通過將子程序放在佇列的前面,以讓子程序先執行,以免父程序執行導致寫時複製,而後子程序執行exec系統呼叫,因無意義的複製而造成效率的下降。

cow詳述:

現在有乙個父程序p1,這是乙個主體,那麼它是有靈魂也就身體的。現在在其虛擬位址空間(有相應的資料結構表示)上有:正文段,資料段,堆,棧這四個部 分,相應的,核心要為這四個部分分配各自的物理塊。即:正文段塊,資料段塊,堆塊,棧塊。至於如何分配,這是核心去做的事,在此不詳述。
現在p1用fork()函式為程序建立乙個子程序p2,

核心:(1)複製p1的正文段,資料段,堆,棧這四個部分,注意是其內容相同。

(2)為這四個部分分配物理塊,p2的:正文段->pi的正文段的物理塊,其實就是不為p2分配正文段塊,讓p2的正文段指向p1的正文段塊,資料段->p2自己的資料段塊(為其分配對應的塊),堆->p2自己的堆塊,棧->p2自己的棧塊。如下圖所示:同左到右大的方向箭頭表示複製內容。

寫時複製技術:核心只為新生成的子程序建立虛擬空間結構,它們來複製于父程序的虛擬究竟結構,但是不為這些段分配物理記憶體,它們共享父程序的物理空間,當父子程序中有更改相應段的行為發生時,再為子程序相應的段分配物理空間。

通過以上的分析,相信大家對程序有個深入的認識,它是怎麼一層層體現出自己來的,程序是乙個主體,那麼它就有靈魂與身體,系統必須為實現它建立相應的實體, 靈魂實體與物理實體。這兩者在系統中都有相應的資料結構表示,物理實體更是體現了它的物理意義。

補充一點:linux cow與exec沒有必然聯絡
ps:實際上cow技術不僅僅在linux程序上有應用,其他例如c++的string在有的ide環境下也支援cow技術,即例如:

string str1 = 「hello world」;

string str2 = str1;

之後執行**:

str1[1]=』q』;

str2[1]=』w』;

在開始的兩個語句後,str1和str2存放資料的位址是一樣的,而在修改內容後,str1的位址發生了變化,而str2的位址還是原來的,這就是c++中的cow技術的應用,不過vs2005似乎已經不支援cow。

fork()函式

標頭檔案

[objc] view plaincopy在code上檢視**片派生到我的**片

#include#include
函式原型

[objc] view plaincopy在code上檢視**片派生到我的**片

pid_t fork( void);
(pid_t 是乙個巨集定義,其實質是int 被定義在#include

fork 與程序管理

fork用於複製程序,建立乙個子程序,被複製的程序稱為父程序,父程序與子程序併發執行 先複製乙個程序控制塊pcb 是乙個結構體,在linux中被稱為程序描述符 再複製程序實體 fork完後,父程序中返回值為pid t型別,值為子程序的pid,子程序中值為0 在兩個程序中,邏輯位址相同,實體地址不一定...

fork 函式和linux程序號

pid t 型別 定義程序號型別 實際型別 我們可找到其定義 1 在centos6.5的 usr include sys type.h中可找到其定義 總結 pid t 就是 int 型 2 fork 函式建立子程序 需要的標頭檔案 include和 include 功能 從乙個已經存在的程序中建立新...

Linux 程序建立 fork

1 fork 函式原型 pid t fork void 函式返回值型別為 pid t,實質是 int 型別,linux 核心2.4.0版本的定義是 typedef int kernel pid t typedef kernel pid t pid t 2 fork 函式的底層實現 fork vfor...