--- title: '操作系統 - 內存管理' disqus: kyleAlien --- 操作系統 - 內存管理 === ## OverView of Content 任何操作系統的運行都是在玩 ++**內存遊戲**++,所以學習 Android 系統也要了解一下 **Linux Kernel 的管理機制** [TOC] ## 概述 內存管理有以下幾個重點,接著會以這幾個重點說明 1. **虛擬內存** 2. **內存分配 &回收** 3. **內存保護** ## 虛擬內存 - Virtual Memory * 現在很多應用動不動就會用到幾 G 的記憶體(Android studio 就是),要如何才能滿足每個應用呢? 1. 最直接的方式就是擴大物理內存(但不是每個人都 $,所以該方法不太可靠) 2. 虛擬內存,虛擬內存的出現是為了大應用程式的運行,基本思想是 * 將外部儲存器的部份空間作為內存拓展(從硬體切割出部份) * 當內存資源不足時,系統會按照一定算法自動挑選 **優先級低的數據塊,將這些 ++不常用的++ 數據存到硬碟中** * 後續若需要用到硬碟中的數據,則系統則會產生 **==缺頁指令==**,並把數據交換為到內存(跟另一個不常使用的數據交換位置) * 這些操作都是由操作系統內核自動完成 ### 邏輯地址 轉 物理地址 - 過程 * 對於開啟段頁式內存管理的裝置而言,**邏輯地址必須經過 ==2 次== 轉換最終才能到達真正的物理地址** * 接下來會說說明 **邏輯地址**、**線性地址**(分段機制)、**物理地址** > ![](https://i.imgur.com/8VYFMgv.png) :::info * 補充 早期機器並沒有任何虛擬地址的概念(早期為 ++**實模式**++ 管理,像是 RTOS),所以為了兼容早期作業系統,裝置在開機時仍使用 **實模式**,直到系統做好準備工作後才切換到相應的 ++**保護模式**++ ::: ### 邏輯地址 - Logical Address * 邏輯地址也稱為 **相對地址**,是 **程序編譯後產生的地址**,邏輯地址分為兩個部份組成,^1^ **Segment Selector(段選擇器)** 、^2^ **Offset(位移)** 1. `Segment Selector`(段選擇器):用於描述邏輯地址的位置(段),**16bit**,格式如下 1. TI(Table Indicator):**用於紀錄各種段描述 Segment Descriptor**,有 0.GDT(Global Descriptor Table)、1.LDT(Local Descriptor Table) 2. INDEX:儲存 GDTR、LDTR 的 **序號**,**TI 用來描述 INDEX** 3. RPL(Request Privilege Level 特權要求) > ![](https://i.imgur.com/Cv9LgrW.png) :::success * 補充 GDT 是全局有效,同時系統也允許各自進程創建自己的本地表(LDT)以增加額外的段 ::: 2. Offset(位移):描述偏移量,**32bit**(CPU 提供了專用的暫存器來紀錄 段選擇器,如下表) | 暫存器名稱 | 描述 | | -------- | -------- | | CS | Code Segment Register,代碼 段暫存器 | | DS | Data Segment Register,全局 or 靜態數據 段暫存器 | | SS | Stack Segment Register,堆棧 段暫存器 | | ES | Extra Segment Register,附加數據 段暫存器 | | FS | 通用段暫存器 | | GS | 通用段暫存器 | ### 線性地址 - Linear Address - 分段機制 * **內存地址是經過 ++分段機制++ 轉換形成的**,其思想如下 1. 根據段選擇子(Segment Selector)中的 TI 得知 Index 字符存在 GDT or LDT 中(如上圖) 2. 通過 Index 的儲存的 GDTR or LDTR 獲得 GDT/LDT 儲存地址(Table 的序號) 3. 到 GDT/LDT table 中找到對應的 **段描述** 4. 根據段描述獲得此段此段的 **基地址** 5. 使用在 table 中找到的基地址 + Offset 偏移量,最後就獲得了 線性地址 > ![](https://i.imgur.com/ZgeYG3w.png) ### 物理地址 - Physucal Address - 分頁機制 * 任何的操作系統,最終都要通過真實的物理地址來訪問內存,以 64KB 內存而言就是 0x0000 ~ 0xFFFF 的地址範圍 > 0xFFFF = (2 Bytes) = (2^16^ - 1) = (65536 - 1) * **分頁機制** 1. 頁(虛擬):與分段機制不同的是,分頁機制的操作對象是 **==固定大小的內存塊==**,這種內存塊又稱為 **`頁`**,**一般情況下頁的大小是 4KB** 2. 頁框(物理):與頁的概念對應,**頁框是對物理內存的最小操作單位**,與頁必須一致 * 下圖 Memory 中存在 4 個頁框(藍色),而線性地址(綠色)會對應到這些物理頁框中,而沒有對應的的頁面(2, 5 頁),則會存在硬碟中 * 當需要 2, 5 頁時系統就會產生 **==缺頁==** 並從硬碟中把需要的資料拿回來,放到頁框中(若是 4 個都滿了,則會取 4 個中最少使用到的頁面轉到硬碟中) > ![](https://i.imgur.com/MXhYCIU.png) ## 保護內存 - Memory Protention 內存越界 or Bug or 程式攻擊,都有可能讓系統中的數具被破壞,保護方法就有如上面說的虛擬內存(每個進程的邏輯地址 & 物理地址不會相互對應,讓進程每辦法訪問到其管轄空間以外的物理內存) ### 內存分配 & 回收 * 內存管理是應用程式最關心的,換句話說就是應用程序開發者 & 系統的交界點 > ![](https://i.imgur.com/OIWBNLS.png) * 以下提及部份 Linux Kernel 所面對的核心問題 1. 保證硬體無關性 跟硬體有相關的內存型號、大小、架構 差異不能影響到 應用程式 2. 動態分配內存 & 回收 像是,如何劃分內存區塊、分配粒度(最小單位)、如何管理和區別以使用 & 尚未使用的內存、如何回收內存 3. 內存碎片 如果內存平凡的分配、回收而沒有整理碎片則會導致內存無法正常被使用(使用內存需要使用連續區塊) * Android 回收內存有分為 ^1^ Native 層、^2^ Java 層 1. Native:像是 c++ 的 malloc/free & new/delete 這種就是手動管理內存,但若系統變大並且程式變多則很容易產生野指針(不易管控),**後來就選用 ++智能指針++** 2. Java:Java 則不須太擔心這個問題,因為底層已經幫你解決調很多這方面的問題 ## Appendix & FAQ :::info ::: ###### tags: `Android 系統` `mmap`