1.Linux 進程在內存數據結構
可以看到一個可執行程序在存儲(沒有調入內存)時分為代碼段,數據段,未初始化數據段三部分:
1) 代碼段:存放CPU執行的機器指令。通常代碼區是共享的,即其它執行程序可調用它。假如機器中有數個進程運行相同的一個程序,那么它們就可以使用同一個代碼段。
2) 數據段:存放已初始化的全局變量,靜態變量(包括全局和局部的),常量。static全局變量和static函數只能在當前文件中被調用。
3) 未初始化數據區(uninitializeddata segment,BSS):存放全局未初始化的變量。BSS的數據在程序開始執行之前被初始化為0或NULL。
代碼區所在的地址空間最低,往上依次是數據區和BSS區,并且數據區和BSS區在內存中是緊挨著的。。
可執行程序在運行時又多出了兩個區域:棧段(Stack)和堆段(Heap)。
4) 棧區:由編譯器自動釋放,存放函數的參數值,局部變量等。每當一個函數被調用時,該函數的返回類型和一些調用的信息被存儲到棧中。然后這個被調用的函數再為它的自動變量和臨時變量在棧上分配空間。每調用一個函數一個新的棧就會被使用。棧區是從高地址位向低地址位增長的,是一塊連續的內在區域,最大容量是由系統預先定義好的,申請的??臻g超過這個界限時會提示溢出,用戶能從棧中獲取的空間較小。
5) 堆段:用于存放進程運行中被動態分配的內存段,位于BSS和棧中間的地址位。由程序員申請分配(malloc)和釋放(free)。堆是從低地址位向高地址位增長,采用鏈式存儲結構。頻繁地malloc/free造成內存空間的不連續,產生碎片。當申請堆空間時庫函數按照一定的算法搜索可用的足夠大的空間。因此堆的效率比棧要低的多。
這個5中內存區域中數據段、BSS和堆通常是被連續存儲的——內存位置上是連續的,而代碼段和棧往往會被獨立存放。有趣的是堆和棧兩個區域關系很“曖昧”,他們一個向下“長”(i386體系結構中棧向下、堆向上),一個向上“長”,相對而生。但你不必擔心他們會碰頭,因為他們之間間隔很大(到底大到多少,你可以從下面的例子程序計算一下),絕少有機會能碰到一起。
下圖簡要描述了進程內存區域的分布:
2. 地址相關概念
1. 物理地址(physical address)
物理內存,真實存在的插在主板內存槽上的內存條的容量的大小.
內存是由若干個存儲單元組成的,每個存儲單元有一個編號,這種編號可唯一標識一個存儲單元,稱為內存地址(或物理地址)。我們可以把內存看成一個從0字節一直到內存最大容量逐字節編號的存儲單元數組,即每個存儲單元與內存地址的編號相對應。
2. 虛擬內存(Virtual memory)(也叫虛擬存儲器)
虛擬內存地址就是每個進程可以直接尋址的地址空間,不受其他進程干擾。每個指令或數據單元都在這個虛擬空間中擁有確定的地址。
虛擬內存就是進程中的目標代碼,數據等虛擬地址組成的虛擬空間
虛擬內存不考慮物理內存的大小和信息存放的實際位置,只規定進程中相互關聯信息的相對位置。每個進程都擁有自己的虛擬內存,且虛擬內存的大小由處理機的地址結構和尋址方式決定。
如直接尋址,如果cpu的有效地址長度為16位,則其尋址范圍0 -64k。
再比如32位機器可以直接尋址4G空間,意思是每個應用程序都有4G內存空間可用。但是顯然機器內存罕有如此之大,可以支持每個程序使用4G內存的。
虛擬內存與物理內存的區別:虛擬內存就與物理內存相反,是指根據系統需要從硬盤虛擬地勻出來的內存空間,是一種計算機系統內存管理技術,屬于計算機程序,而物理內存為硬件。因為有時候當你處理大的程序時候系統內存不夠用,此時就會把硬盤當內存來使用,來交換數據做緩存區,不過物理內存的處理速度是虛擬內存的30倍以上。
3. 邏輯地址(logical address)
源程序經過匯編或編譯后,形成目標代碼,每個目標代碼都是以0為基址順序進行編址的,原來用符號名訪問的單元用具體的數據——單元號取代。這樣生成的目標程序占據一定的地址空間,稱為作業的邏輯地址空間,簡稱邏輯空間。
在邏輯空間中每條指令的地址和指令中要訪問的操作數地址統稱為邏輯地址。即應用程序中使用的地址。要經過尋址方式的計算或變換才得到內存中的物理地址。
很簡單,邏輯地址就是你源程序里使用的地址,或者源代碼經過編譯以后編譯器將一些標號,變量轉換成的地址,或者相對于當前段的偏移地址。
邏輯地址是指由程序產生的與段相關的偏移地址部分。例如,你在進行C語言指針編程中,可以讀取指針變量本身值(&操作),實際上這個值就是邏輯地址,它是相對于你當前進程數據段的地址,不和絕對物理地址相干。只有在Intel實模式下,邏輯地址才和物理地址相等(因為實模式沒有分段或分頁機制,Cpu不進行自動地址轉換);邏輯也就是在Intel保護模式下程序執行代碼段限長內的偏移地址(假定代碼段、數據段如果完全一樣)。應用程序員僅需與邏輯地址打交道,而分段和分頁機制對您來說是完全透明的,僅由系統編程人員涉及。應用程序員雖然自己可以直接操作內存,那也只能在操作系統給你分配的內存段操作。
不過有些資料是直接把邏輯地址當成虛擬地址,兩者并沒有明確的界限。
在linux內核,虛擬地址是3G-4G這段地址,它與物理地址通過頁表來映射,邏輯地址是指3G-3G+main_memory_size這段虛擬地址,它與物理地址的映射是線性的,當然也可以通過頁表映射。所以邏輯地址是虛擬地址的一部分。
邏輯地址的組成:是由一個段標識符加上一個指定段內相對地址的偏移量,表示為 [段標識符:段內偏移量]
圖4.1 作業的名空間、邏輯地址空間和裝入后的物理空間
4. 線性地址或Linux下也叫虛擬地址(virtual address)
這個地址很重要,也很不容易理解。分段機制下CPU尋址是二維的地址即,段地址:偏移地址,CPU不可能認識二維地址,因此需要轉化成一維地址即,段地址*16+偏移地址,這樣得到的地址便是線性地址(在未開啟分頁機制的情況下也是物理地址)。這樣有什么意義呢?或者說這個一維地址的計算方法隨便一個學計算機的人都知道,但是你真的理解它的意思嗎?要想理解它的意思,必須要知道什么是地址空間,下文詳述。
線性地址是邏輯地址到物理地址變換之間的中間層。程序代碼會產生邏輯地址,或者說是段中的偏移地址,加上相應段的基地址就生成了一個線性地址。如果啟用了分頁機制,那么線性地址可以再經變換以產生一個物理地址。若沒有啟用分頁機制,那么線性地址直接就是物理地址。Intel 80386的線性地址空間容量為4G(2的32次方即32根地址總線尋址)。
跟邏輯地址類似,它也是一個不真實的地址,如果邏輯地址是對應的硬件平臺段式管理轉換前地址的話,那么線性地址則對應了硬件頁式內存的轉換前地址。
CPU將一個虛擬內存空間中的地址轉換為物理地址,需要進行兩步:首先將給定一個邏輯地址(其實是段內偏移量=),CPU要利用其段式內存管理單元,先將為個邏輯地址轉換成一個線程地址,再利用其頁式內存管理單元,轉換為最終物理地址。
3.地址映射
由 I NTEL公 司 推 出 的 32 位 80386 芯 片 的 工 作 模 式 包 括 實地址模式和虛地址模式 。 實地址模式與8086 完全兼容 . 它的 尋址范圍是1 MB的地址空間. 分段功能受到限制 .不能區分特權級 . 當然分頁機制也不能啟用 。 在虛地址模式下. 分段機 制得到加強 . 段最大可達4GB. 并且提供段 內分頁管理機制 . 為 Linux虛擬內存管理機制提供了支持 。
80386 的虛擬地址模式使用了如下分段和分頁兩級地址 轉換機制來實現虛擬地址向物理地址的轉換 。
2 .1 虛擬地址向線性地址的轉換
用戶進程要訪問的虛擬地址包括一個1 6 位的段選擇器和 一個32 位的段 內偏移 . 80386 的分段機制將段寄存器中所裝的
段選擇器和32 位段 內偏移量相加. 得到32位的線性地址 , 如圖1所示 , 16 位的段選擇器最低兩位表示請求者的特權級 , 那么
最多可以有16k個段 , 每段的最大尺寸為4GB。 但是它們都必 須被映射到4GB的線性地址空間。
圖1
2 .2 線性地址向物理地址的轉換
Linux的每個用 戶進 程都可 以訪 問4 GB的線 性地址空間, 而實際的物理 內存可能遠 遠少于4GB. 采用分頁機制 。 Linux僅把可執行映像的一小部分 裝入物理 內存. 當需要訪問未裝入的頁面時 . 系統產生一個缺頁中斷 , 把需要的頁讀入 物理內存。
圖2
Linux采用兩級頁表結構—— 頁目錄表和頁表實現地址 映射. 當前正在運行進程的頁 目錄表的地址被保存在控制寄
存器 CR3 中。 由上面轉換機制所得到的線性地址可以分為3 部分 , 高 10位是 DI R域—— 頁 目錄表的索引值 . 它與 CR3 中的地址一起 計算得到頁表的物理地址 . 中間1O位保 存相對于頁表的索引 值 . 通過它得到所需的物理頁號。 物理頁號與低1 2 位頁內偏移 組合得到物理地址 。 其結構如圖2 所示 。
4.虛擬地址管理
每個用戶進程都可以有4 GB的虛存空 間. 為了更好地管 理這部分虛存空間. Linux主要定義了如下三個數據結構 :
struct vm_area_struct ,
struct vm_operations_struct
struct vmm_struct
虛存段( vm_area_struct ) . 簡稱 vma是某個進程的一段 連續的虛存空間. 一個進程通常占用幾個 vma段 . 例如代碼段 、 數據段、 堆棧段等 。 vma不僅可以代表一段內存區間, 也可 以對應于一個文件、 共享內存或者對換設備。
每一個進程的所有 vma由一個雙向鏈表管理。 為了提高 對 vma的查詢、 插入、 刪除等操作的效率 . Linux把系統中所 有進程的 vma組成了一棵 AVL樹。 這是一棵平衡二叉樹 . 當 vma數量特別 大時。 利用這棵 AVL樹查找 v ma的效率得到 明顯提 高 。
不同的 vma可能需要不同的操作處理方式 . 但同時考慮到統接口的統 一 性 . Linux采 用vm_operations_struct結 構和面向對象的思想來定義操作方式 . 一個vm_operations_struct結構體是一組 函數指針 , 對于不同的 vma . 它可能指向 不同的處理 函數.例如當發生缺頁錯誤時 . 共 享內存和代碼 段 的 readpage所 指 向的頁面讀入函數可能就不同 。
內存管理中另外 一個 非 常重要的數 據 結 構是vmm_struct 結構體 .進程 的 task_struct中的mm成員指向 它. 當前運行進程的整個虛擬空間都 由它來管理和描述 . 它不僅包含該進程的映像信 息. 而且它的 mma p成員項指向該進 程所有vma組成的鏈 表 。 它的 mmap_avl 成 員 項 指 向整個系統 的 AVL樹 。
這三個數據結構之間相互關聯. 共同管理虛擬內存 . 它們之 間的 關 系如圖 3所 示 。
圖 3
這部分相關的系統調用主要有如下兩個 :
do_mmap( struct file *file, unsigned long addr unsigned long len , unsigned long prot , unsigned long flags , unsigned long off ); find_vma ( struct mm_struct mm , unsigned long addr );
do _mmap函數實現了 內存映射 。 find_vma函數的功能 是找到包含參數 addr指定的虛擬地址所屬的 vma 。 當要運行一個可執行映像時 . 調用 do _mmap將其裝入 到該進程 的虛擬地址空間 . 并且產生一組 vma結構 . 如前所 述該進程的整個虛擬空間由 vmm_struct 結構管理 . 但是此時 可執行文件僅僅被連接到進程的虛擬空間中. 只有一小部分 頁面被裝入到物理 內存 . 其余大部分并沒有被真正裝入到物 理內存 . 在進程的運行過程 中. 產生缺頁錯誤 . 操作 系統首先調用 find_vma. 找到該虛擬地 址所在的 vma . 然后根據該 vma的成員變量 vm_ops指向的vm_operations_struct結構 中的缺頁操作 函數。 把頁裝入物理內存。 ·
5.對換空間
Linux的每個進程可以有4 GB的虛擬 內存空間 . 而且系統中還要同時存在多個進程 ,但是 ,事實上大多數計算機都沒 有這么多物理內存空間 , 當系統中的物理內存緊缺時 . 就需要利用對換空間把一部分未來可能不用的頁面從物理內存中移 到對換設備或對換文件中。
Linux采用兩種方 式保存換 出的頁面 。 一種是利 用整個塊設備 , 如硬盤的一個分區 . 即對換設備, 另一種是利用文件系統中固定長度的文件 . 即對換文件。 它們統稱為對換空間。 這兩種方式的相同之處是它們的內部格式一致. 但是在執行效率方面 . 對換設備要好一些. 這是因為對換設備上同一頁面 的數據塊是連續存放的 . 故而可 以順序存取 , 而在對換文件
中 。 同一頁面的數據塊實際的物理位置可能是不連續的 . 需要通過對換文件的 i n o d e檢索. 這就降低了存取效率 。
每個對換文件或對換設備 由 s t r u c t s wa p — i n f o — s t r u c t 結構來描述。 有關對換設備的 函數主要是 g e t — s wa p — p a g e ( …) . 當 內 存中的頁面需要被換 出時 . 調用 g e t — s wa p —p a g e函數 申請得到一個對換空間中的物理頁面 。 如果成功, 就返 回一個非零代 碼 . 否則 . 返 回0。
6.分頁機制管理
L i n u x使用分頁管理機制來更加有效地利用物理 內存 . 當創建一個進程時 . 僅僅把當前進程的一小部分真 正裝入內
存. 其余部分需要訪問時 . 處理 器產生一個頁故障 . 由缺頁中 斷服務程序根據缺頁虛擬地址和出錯碼調用寫拷貝函數 d o —
wp —p a g e 、 此 地址 所 屬 的 v ma的 v m—o p s指 向 的 n o p a g e 、 d o — s wa p — p a g e . s wa p — i n等函數將需要的頁換入物理內存 。 隨著可執行映像 的運行和頁面的換入 . 系統 中的內存有 可能變得不足. 這時 L i n u x核心就必須調用 k s wa p d守護進 程釋放部分物理 內存 。k s wa p d在 系統啟動時 由 i n i t進程 建 立。 在系統的運行過程中 。 它被定期喚醒 。 檢查系統中的空閑 物理 內存是否很少 。 如果是. 則釋放一部分內存. 或者將一些 頁 面換 出到對 換 空間 。 然 后繼 續睡 眠 。
5 . 1 缺頁中斷和頁面換入
頁 面換 入 主 要 由缺 頁 中斷 服 務 入 口 函數 d o —p a g e —f a u l t 來實現 。 當系統中產生頁面故障時 . 如果虛擬內存地址有效 . 則產生錯誤的原因有如下兩種 :
虛擬內存地址對應的物理 頁不在 內存 中。 那么它必然在 磁盤或 對換 空 間中. 如果 在磁盤 上. 那么我們 調用 d o — n O —
p a g e函 數 . 而 d o —n o —p a g e調 用 v ma 一 > v m—o p s 一 > n op a g e () 函數建立頁面映射 . 從對換空間或磁盤中調入頁面 . 或者通過 d o — s wa p — p a g e ( ) 函數調用 s wa p —i n ( ) 來換入頁面。
該虛擬地址對應的物理頁在內存 。 但是被寫保護. 如果 這種情況發生在一個共享頁面上 . 則需要“ 寫拷 貝“ 函數 d o —
wp —p a g e來換 入 頁 面.d o — wp — p a g e函數 首 先 調 用一 g e t — f r e e — p a g e獲得一新 頁面 . 然 后調用 c o p y — C O W— p a g e拷貝頁 面的內容. 當然還要調用相應的刷新 函數刷新 TL B和緩存 等 。
5 . 2 頁交換進程和頁面換出
正如我們上面所描述的 . 系統使 用 k s wa p d守護進程來 定期 地 換 出頁 面。 使 系 統 中 有足 夠 的空 閑物 理 內存 頁。
k s wa p d進程定期地檢查系統中的空閑頁面數 . 如果少于一定 值 . 則按照以下三中途徑獲得空閑頁面 : ①減少緩沖區和頁面
高速緩存的大??; ②把共享 內存 占用的頁面置換到對換空 間;
③換出或丟棄物理內存頁。
轉自:http://blog.csdn.net/hguisu/article/details/6152921
原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/3128