內存映射單獨拿出來說,因為很有趣,不僅關系到共享庫,還有fork等
什么叫內存映射?
- linux通過將一個虛擬內存區域與一個磁盤上的對象關聯起來,也就是一個文件到一塊內存的映射。
共享對象
我們已經知道,進程這一抽象能夠為每個進程提供自己私有的虛擬地址空間,但是如果許多進程有同樣的只讀代碼區域呢?比如相同的庫函數,如果每個進程擁有一份副本,那太浪費空間了,所以內存映射提供了機制來控制多個進程共享對象。
共享對象.png
寫時復制
私有對象一開始與共享對象一樣,在物理內存中只保留一份副本。進程中相應私有區域的頁表條目都被標記為只讀,并且區域結構被標記為私有的寫時復制,只要沒有進程試圖寫私有區域,那么就繼續共享物理內存中的對象副本。一旦一個進程試圖寫私有區域內的某個頁面,就會觸發一個保護故障。
當故障處理程序發現保護異常時由于進程試圖寫私有的寫時復制區域的一個頁面引起的,就會在物理內存中創建這個頁面的一個副本,更新頁表條目指向這個新的副本,然后恢復這個頁面的可寫權限。
延遲私有對象的副本直到最后時刻,寫時復制最充分的利用了稀有的物理內存
私有對象寫時復制.png
fork函數
那么現在我們可以深刻理解fork函數了:
當fork函數被當前進程調用時,內核為新進場創建各種數據結構,并分配唯一的PID。為了給這個新進程創建虛擬內存,它創建了當前進程的mm_struct、區域結構和頁表的原樣副本。將兩個進程中的每個頁面都標記為只讀,并將兩個進程中的每個區域結構都標記為私有的寫時復制。
當兩個進程中的任一個后來進行寫操作時,寫時復制機制就會創建新頁面,也就為每個進程保持了私有地址空間的概念。
execve函數
execve("a.out", NULL, NULL); //在當前進程中加載并運行a.out
以下幾個步驟:
- 刪除已存在的用戶區域
- 映射私有區域,為新程序的代碼、數據等創建新的區域結構,都是私有的寫時復制的。
- 映射共享區域,把動態鏈接庫映射到共享區域
- 設置程序計數器(PC),使之指向代碼區域的入口點。
mmap函數
linux進程使用mmap函數來創建新的虛擬內存區域,并將對象映射到這些區域中。具體就不管啦。