一,內存管理
1,操作系統的內存管理分爲三部分:
- 物理內存的管理
- 虛擬內存的管理
- 虛擬地址和物理地址如何映射
2,虛擬空間分爲兩部分:
- 內核空間
- 用戶空間
- Text segment存放二進制可執行代碼的位置
- data segment存放靜態常量
- bss segment存放未初始化的靜態變量
- 堆動態內存分配,往高地址增長
- memory mapping segment:文件映射進內存,比如動態庫
- 棧往低地址增長
總結:
- 虛擬內存空間的管理,每個進程看到的都是獨立的,互不干擾的虛擬地址空間
- 物理內存的管理,物理內存地址只有內存管理模塊能夠使用
- 內存映射,需要將虛擬內存和物理內存映射、關聯起來
3,分段管理
(1)分段機制
- 虛擬地址 = 段選擇子(段寄存器) + 段內偏移量
- 段選擇子 = 段號(段表索引) + 標識位
- 段表 = 物理基地址 + 段界限(偏移量範圍) + 特權等級
如果要訪問段2中偏移量600的虛擬地址,我們可以計算出物理地址=段2的基地址2000+偏移量600=2600
(2)Linux 分段實現
- 段表稱爲段描述符表, 放在全局標識符表GDT中
- Linux 將段基地址都初始化爲 0, 並沒有使用段的全部功能,不用於地址映射
- Linux 分段功能主要用於權限檢查
4,Linux 通過分頁實現映射
- 物理內存被換分爲大小固定(4KB)的頁, 物理頁可在內存與硬盤間換出/換入,提高內存的利用率
- 頁表 = 虛擬頁號 + 物理頁號; 用於定位頁
- 虛擬地址 = 虛擬頁號 + 頁內偏移
- 若採用單頁表, 32位系統中一個頁表將有 1M 頁表項, 佔用 4MB(每項 4B)
- Linux 32位系統採用兩級頁表: 頁表目錄(1K項, 10bit) + 頁表(1K項, 10bit) + (頁大小(4KB, 12bit)),正好對應32位
- 映射 4GB 內存理論需要 1K 個頁表目錄項 + 1K*1K=1M 頁表項, 將佔用 4KB+4MB 空間
- 因爲完整的頁表目錄可以滿足所有地址的查詢, 因此頁表只需在對應地址有內存分配時才生成;
- linux64 位系統採用 4 級頁表
採用兩級頁表的好處
總結:
- 虛擬內存空間的管理,將虛擬地址分成大小相等的頁;
- 物理內存的管理,將物理內存分成大小相等的頁;
- 內存映射,將虛擬內存和物理內存映射起來,並且在內存緊張的時候可以換出到磁盤
二,進程空間管理
- 內存管理信息在 task_struct 的 mm_struct 中
- task_size 指定用戶態虛擬地址大小。32 位系統:3G 用戶態, 1G 內核態。64 位系統(只利用 48 bit 地址): 128T 用戶態; 128T 內核態
(1)用戶態地址空間佈局和管理
- mm_struct 中有映射頁的統計信息(總頁數, 鎖定頁數, 數據/代碼/棧映射頁數等)以及各區域地址,用戶態區域佈局如下圖
- mm_struct中還有 vm_area_struct 描述各個區域(代碼/數據/棧等)的屬性(包含起始/終止地址, 可做的操作等), 通過鏈表和紅黑樹管理
(2)內核地址空間佈局和管理
- 所有進程看到的內核虛擬地址空間是同一個
- 32 位系統, 前 896MB 爲直接映射區(虛擬地址 - 3G = 物理地址),直接映射區也需要建立頁表, 通過虛擬地址訪問(除了內存管理模塊),直接映射區組成: 1MB 啓動時佔用; 然後是內核代碼/全局變量/BSS等,即 內核 ELF文件內容; 進程 task_struct 即內核棧也在其中,896MB 也稱爲高端內存(指物理內存),剩餘虛擬空間組成: 8MB 空餘; 內核動態映射空間(動態分配內存, 映射放在內核頁表中); 持久內存映射(儲存物理頁信息); 固定內存映射; 臨時內存映射(例如爲進程映射文件時使用)
- 64 位系統: 8T 空餘; 64T 直接映射區域; 32T(動態映射); 1T(物理頁描述結構 struct page); 512MB(內核代碼, 也採用直接映射)
三,物理內存管理
(1)物理內存組織方式
- 每個物理頁由 struct page 表示
- 物理頁連續, page 放入一個數組中, 稱爲平坦內存模型,多個 CPU 通過總線訪問內存, 稱爲 SMP 對稱多處理器(採用平坦內存模型, 總線成爲瓶頸)
- 每個 CPU 都有本地內存, 訪問內存不用總線, 稱爲 NUMA 非一致內存訪問,本地內存稱爲 NUMA 節點, 本地內存不足可以向其他節點申請,NUMA 採用非連續內存模型,頁號不連續
(2)節點
- 用 pglist_data 表示 NUMA 節點,每個節點分成一個個區域zone,多個節點信息保存在 node_data 數組中
- pglist_data 包括 id,page 數組,起始頁號, 總頁數, 可用頁數
- 節點分爲多個區域 zone, 包括 DMA; 直接映射區; 高端內存區; 可移動區(避免內存碎片)
- 區域 zone,包含第一個頁頁號; 區域總頁數; 區域實際頁數; 被夥伴系統管理的頁數; 用 per_cpu_pageset 區分冷熱頁(熱頁, 被 CPU 緩存的頁)
(3)頁
- 用 struct page 表示, 有多種使用模式, 因此 page 結構體多由 union 組成
- 使用一整個頁: 1) 直接和虛擬地址映射(匿名頁); 2) 與文件關聯再與虛擬地址映射(內存映射文件);page 記錄: 標記用於內存映射; 指向該頁的頁表數; 換出頁的鏈表; 複合頁, 用於合成大頁;
- 分配小塊內存:Linux 採用 slab allocator 技術; 申請一整頁, 分爲多個小塊存儲池, 用隊列維護其狀態(較複雜);slub allocator 更簡單;slob allocator 用於嵌入式; page 記錄: 第一個 slab 對象; 空閒列表; 待釋放列表
(4)頁分配
- 分配較大內存(頁級別), 使用夥伴系統
- Linux 把空閒頁分組爲 11 個頁塊鏈表, 鏈表管理大小不同的頁塊(頁大小 2^i * 4KB)
- 分配大頁剩下的內存, 插入對應空閒鏈表
總結: