Linux進程學習(總結學習關於“寫時複製”)

首先是對進程的理解定義:

進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎。在早期面向進程設計的計算機結構中,進程是程序的基本執行實體;在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。


以上是百度的進程的概念。

我的理解就是,它是程序運行的過程,是cpu進行資源調度的基本單位,是線程的容器,是操作系統對系統資源的抽象。

linux系統通過PCB(PROCESS CONTROL BLOCK)來控制進程.

在linux中每個進程都有唯一的進程ID來標示進程,雖然是唯一的,但是確實可複用的。當一個進程終止時,他的ID就成爲了複用的候選ID。

ID爲0的進程,是系統內核進程。ID爲1的進程,是init進程,在自舉過程結束後由內核調用。該進程的程序文件在/sbin/init中,他負責系統初始化的一些東西。

在Linux中,進程不是相互獨立的,每個進程(除了init進程)都有一個父進程(parent process),同時每個進程可以有0個1個或多個子進程(child process)。換句話說,Linux的進程是一個樹形結構,在Shell下輸入pstree可以查看這個樹的形狀。

創建新進程:

一個現有的進程,使用fork可以創建新進程。

fork被調用一次,但是會返回兩次。其返回兩次的本質是:父進程與子進程有不同的各自的內存空間,返回值被返回到了兩個進程的各自的內存空間。

返回值如果是-1的話,就說明創建失敗。

返回值0是返回給子進程的。

返回值大於0的值是子進程的進程ID,返回給父進程。


爲什麼將子進程的ID返回給父進程?

因爲父進程可以有多個子進程,並且沒有相關的函數可以讓父進程得到他所有子進程的ID。

爲什麼將0返回給子進程?

因爲子進程可以使用getpid()得到父進程的ID,而進程ID0總是由內核交換進程使用,子進程的ID不可能爲0.


在fork之後,子進程到底從父進程哪裏得到了什麼:

一般情況下,我們認爲,子進程會得到父進程的堆,棧,數據段的副本,注意是副本,但是他們會共享代碼段。

由於,獲得了堆棧的複製,所以變量的地址(邏輯地址)應該也是一樣的。

每個進程都有自己的虛擬地址空間,不同進程的相同的虛擬地址顯然可以對應不同的物理地址。因此地址相同(虛擬地址)而值不同沒什麼奇怪。

不過,事實遠非那麼簡單,linux使用了COW(Copy-On-Write)技術。即“寫時複製”技術,也就是隻有進程空間的各段的內容要發生變化時,纔會將父進程的內容複製一份給子進程。這是由於,fork之後一般我們會使用exec,所以很多的實現並不執行一個父進程的完全的副本。

再說寫時複製之前,需要理解邏輯地址和物理地址。

從邏輯地址到物理地址的映射稱爲地址重定向。分爲:

靜態重定向--在程序裝入主存時已經完成了邏輯地址到物理地址和變換,在程序執行期間不會再發生改變。

動態重定向--程序執行期間完成,其實現依賴於硬件地址變換機構,如基址寄存器。

邏輯地址:CPU所生成的地址。CPU產生的邏輯地址被分爲 :p (頁號) 它包含每個頁在物理內存中的基址,用來作爲頁表的索引;d (頁偏移),同基址相結合,用來確定送入內存設備的物理內存地址。

物理地址:內存單元所看到的地址。

用戶程序看不見真正的物理地址。用戶只生成邏輯地址,且認爲進程的地址空間爲0到max。物理地址範圍從R+0到R+max,R爲基地址,地址映射-將程序地址空間中使用的邏輯地址變換成內存中的物理地址的過程。由內存管理單元(MMU)來完成


在fork之後exec之前兩個進程用的是相同的物理空間(內存區),子進程的代碼段、數據段、堆棧都是指向父進程的物理空間,也就是說,兩者的虛擬空間不同,但其對應的物理空間是同一個。當父子進程中有更改相應段的行爲發生時,再爲子進程相應的段分配物理空間,如果不是因爲exec,內核會給子進程的數據段、堆棧段分配相應的物理空間(至此兩者有各自的進程空間,互不影響),而代碼段繼續共享父進程的物理空間(兩者的代碼完全相同)。而如果是因爲exec,由於兩者執行的代碼不同,子進程的代碼段也會分配單獨的物理空間。(總結就是,因爲exec的使用,導致了父子進程,不能共享代碼段了).


寫時複製的過程:fork子進程完全複製父進程的棧空間,也複製了頁表,但沒有複製物理頁面,所以這時虛擬地址相同,物理地址也相同,但是會把父子共享的頁面標記爲“只讀”(類似mmap的private的方式),如果父子進程一直對這個頁面是同一個頁面,知道其中任何一個進程要對共享的頁面“寫操作”,這時內核會複製一個物理頁面給這個進程使用,同時修改頁表。而把原來的只讀頁面標記爲“可寫”,留給另外一個進程使用。


fork出來子進程之後,父子進程哪個先調度呢?

內核一般會先調度子進程,因爲很多情況下子進程是要馬上執行exec,會清空棧、堆。。這些和父進程共享的空間,加載新的代碼段。。。,這就避免了“寫時複製”拷貝共享頁面的機會。如果父進程先調度很可能寫共享頁面,會產生“寫時複製”的無用功。所以,一般是子進程先調度滴。


總結就是:剛開始,虛擬地址相同,物理地址也相同。有修改操作時,就變成了,虛擬地址相同,物理地址不同。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章