1. 程式人生 > >今日刷題總結19

今日刷題總結19

磁盤 地址空間 strong 進程創建 創建 數據 進行 程序 堆棧

共享區域與私有區域

每個進程都有自己的私有虛擬地址空間,避免了受到其他進程的錯誤讀寫。但是,通常的c程序幾乎都使用到標準庫函數,例如printf或者scanf,如果每個進程都要為這些常用庫函數在物理內存保留一份拷貝,這樣對內存就非常浪費了。

為了解決上述問題,可以將常用庫函數設定為共享對象,共享對象在物理內存上只有一份拷貝,多個進程可以把自身虛擬內存的一個區域映射到該共享對象上,這些區域就叫共享區域,如果一個進程在自己的共享區域進行寫操作,在其他進程的共享區域內能看到相應的改變,並且這種改變也會反映到磁盤上。

除了共享對象,還存在私有對象,一個進程可以將自身虛擬內存的一個區域映射到私有對象上,該區域就叫私有區域,對私有區域做出改變,對於其他進程來說是不可見的,而且對這種改變不會反映到磁盤上。

寫時拷貝

私有對象使用寫時拷貝被映射到虛擬內存上。私有對象開始時的存在方式基本和共享對象一樣,即私有對象在物理內存上只有一份拷貝,多個進程將自己虛擬內存的一個區域(這些區域的地址範圍不必相同)映射到私有對象上,各進程對應私有對象的頁表條目也被標記為只讀,並且區域結構被標記為私有的寫時拷貝。各進程共享在物理內存上的私有對象,但如果一個進程修改了私有區域的某一頁,會觸發一個保護故障,內核的故障處理程序就會為這個頁面創建一份新的拷貝,並更新頁表條目指向這個新的拷貝,然後恢復這個頁面的寫權限。當故障處理程序返回時,cpu在新創建的頁面上重新執行這個寫操作。

fork和execve

父進程調用fork創建子進程時,內核為子進程分配一個唯一的pid,並拷貝了父進程的mm_struct,區域結構(vm_area_struct)以及頁表,將父子進程每個頁面設置為只讀,區域結構設置為私有的寫時拷貝。fork返回會,子進程就具有了和父進程相同的虛擬地址空間。當其中一個進程對自己的虛擬內存進行寫操作時,寫時拷貝機制就會為該進程創建新頁面。

假設子進程接著執行execve("a.out",NULL,NULL),該函數會加載並運行可執行目標文件a.out中的程序,用a.out程序去替代子進程,加載的過程如下:

1)刪除子進程虛擬內存用戶部分的區域結構。

2)為a.out程序的代碼段,數據段,堆棧區域創建新的區域結構,區域結構被設置為私有的寫時拷貝。

3)如果a.out與共享對象鏈接,則這些對象都是動態鏈接到這個程序的,然後映射到虛擬內存的共享區域。

4)execve設置程序計數器PC,使其指向代碼段的入口點。

今日刷題總結19