LInux進程虛擬地址空間的管理


2017-04-07

脫離物理內存的管理,今天咱們來聊聊進程虛擬內存的管理。因為進程直接分配和使用的都是虛擬內存,而物理內存則是有系統“按需”分配給進程,在進程看來,只知道虛擬內存的存在!


  前言:

  關於虛擬內存和物理內存這些東東,本篇不作介紹,此等基礎知識參考最普通的操作系統參考書籍即可。當然有問題也可留言,我們共同學習,共同進步!

進程虛擬地址空間:

  每個進程擁有一個獨立的虛擬地址空間,獨立怎么體現?進程A有個地址1000,進程B也有個地址1000,為何兩個地址不發生矛盾,還能正常工作,這就是獨立性的體現。原來,兩個相同的虛擬地址會經過自己所屬進程的頁表,轉化成不同的物理地址。只要實際的物理地址不發生沖突,就沒有關系。這一切怎么實現的呢?自然需要內核去維護。內核為每個進程維護一套頁表,頁表實現虛擬地址到物理地址的轉換,其基地址存儲在CR3寄存器中,是物理地址。x86架構下,地址線是32根,那么就可以尋址到2^32個地址,因為默認都是按字節尋址,所以虛擬地址空間就為2^32字節即4GB,64位額CPU使用48根地址線,虛擬地址空間更大。本文不做虛擬地址空間深入分析,旨在介紹虛擬地址空間是怎么使用的。

虛擬地址空間的划分:

每個進程都有自己的虛擬地址空間,但是也不能隨意使用,還是需要規划好的,畢竟內存時比較稀缺的資源,咱們必須本着可持續發展的戰略去使用。很明顯,進程運行過程中需要的信息都要裝入內存,那么進程運行期間都需要哪些信息呢?

  • 程序運行代碼的二進制代碼。
  • 程序使用的動態庫的代碼。
  • 存儲全局變量和動態產生數據的堆。
  • 保存局部變量和實現函數過程調用的棧。
  • 環境換量和命令行參數的段。
  • 將文件內容映射到虛擬地址空間的的內存映射。

進程虛擬地址空間映射結構如下兩種結構,在地址空間不富裕的情況下,前者比較合適。

 

進程的3GB空間安排大致如圖所示,最底下是映射的可執行文件的映像,一般包含代碼段和數據段。數據段中包含有全局變量和靜態數據的空間。再往上就是堆空間,這里堆空間和MMAP區域是相對生長的,從而充分利用這些地址空間。MMAP區域上邊是棧空間,而棧空間上面不使用了,所以棧一般位於進程地址空間的頂部。一般情況下棧的最高地址是用戶空間可用的最高地址,當指定棧隨機化時,會空閑一個隨機大小,這樣可在一定程度上,對棧實施保護。

每個進程結構有一個mm_struct結構管理該進程的地址空間,每個分配的內存塊由一個vm_area_struct結構表示,所有的vm_area_struct結構構成一個紅黑樹,樹根保存在mm_struct中的mm_rb字段。這點和windows的進程地址空間管理十分類似,windows下采用的是VAD樹,也是個二叉樹。在vm_area_struct結構中記錄該內存塊的起始和結束地址,同一個進程的所有vm_area_struct結構還會連接成一個鏈表,開始於mm_struct 中的mmap。總體來講進程地址空間的分配主要涉及三部分:進程可執行文件的映射、堆棧的分配、MMAP區域的分配。

進程可執行文件的映射

可執行文件映射在圖中的text段,這里text段包含代碼、數據。代碼自然是程序自身的代碼,而數據就是一些常量,全局數據區,靜態數據區等。具體映射方式需要根據ELF文件的格式,所以映射的細節我們不做詳細介紹。當可執行文件的映像映射到進程的虛擬地址空間時,產生一組vm_area_struct結構,相當於還沒執行之前,可執行文件已經被划分成了虛擬頁面,由vm_area_struct管理。待程序執行時會根據vm_area_struct結構和物理內存建立映射並填充頁表項,具體參見pagefault處理流程。

堆棧的分配

堆棧在程序運行時動態分配,二者的用途不同。堆主要用於動態內存的分配,可分配比較大的內存塊,且必須主動申請分配,使用完后需要手動釋放如malloc、new(malloc在分配更大的內存會在MMAP區域)。而棧有程序自動分配,有系統負責回收,且棧的大小一般受限,常分配比較小的內存塊。

MMAP區域

MMAP 區域位於堆和棧之間,用於映射文件內容到進程虛擬地址空間。注意這里是虛擬地址空間而不是物理地址空間。 每個文件在內核對應一個inode節點,inode節點的 i_mapping字段是address_space類型,記錄當前文件到進程地址空間的映射情況。address_space結構中關聯了映射的內存區域vm_area_struct,私有和共享映射通過紅黑樹管理,而非線性映射通過鏈表管理。進程每打開一個文件,內核會使用一個file結構記錄本次打開的情況,file結構通過f_mapping字段指向文件對應的address_space,這樣進程和文件的聯系就建立起來了。具體聯系如下

我們提到的映射都是映射的是文件到進程虛擬地址空間,不牽涉物理地址。具體物理地址的映射由內存管理單元負責,當進程對文件發起讀寫操作時,最終會根據文件描述符定位到其address_space,根據address_space得到要讀寫的頁,此時頁具備虛擬地址,通過對虛擬地址進行讀寫發生pagefault,此時當前進程就被迫陷入到內核異常處理流程,pagefault處理流程就把本次需要的頁調入內存,然后返回到用戶空間繼續剛才進程的運行,這里其實進程本身並感知不到缺頁,它僅僅負責從文件讀,具體缺頁由CPU感知且處理。關於MMAP其實設計的東西很多,這里不再深入介紹,回頭重開一篇文章單獨講述MMAP機制。

 

參考資料:LInux內核3.10.1源碼、深入LInux內核架構

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM