參考資料
Linux Agent 采集項說明 - 內存
Memory – Part 1: Memory Types
Memory – Part 2: Understanding Process memory
ipcs深入分析
虛擬內存
現代操作系統中, 每一個進程都都存活在各自的內存空間內. 但是操作系統並沒有直接把內存地址對應硬件地址, 而是提供了一個硬件抽象層, 而為每一個進程創造了虛擬內存. 物理內存和虛擬內存之間的映射關系, 是由CPU 使用一個映射表轉換得到的, 這個表由內核維護, 每一個進程在表中有一條記錄.
虛擬內存有多個目的. 第一, 允許進程隔離. 一個進程只能夠與虛擬內存中內存溝通. 這樣它只能存取自己這個進程所關聯的數據, 不能看到其他進程的內存數據. 第二, 硬件抽象. 內核可以自由的更改物理內存地址和虛擬內存的映射關系. 它也可以選擇不給虛擬內存提供任何真實的物理內存, 除非真的需要. 而且它可以在虛擬內存長時間未使用, 而且物理內存吃緊的時候交換到硬盤上. 總的來說, 虛擬內存的存在使得內核更加自由, 唯一的限制是, 當程序需要讀取內存的時候需要換算出原本數據寫在哪兒. 第三, 它可以把不在RAM 中的存儲單位當做內存. 這是mmap 和映射文件的原理. 可以把一個文件當做一段內存, 這樣存取這個文件就像一段內存緩沖區. 這使得寫代碼比較簡潔. 第四, 為了共享. 既然內核知道有哪些進程已經映射到虛擬內存中了, 就可以避免重復加載數據到內存, 而是重復利用指向物理內存的虛擬地址. 共享的結果就是內核使用COW (copy on write): 當兩個進程使用同一份數據, 但是當其中一個更改了數據, 另一個進程不允許看到改變, 內核就會復制一份改變前的數據(類似Oracle 數據庫的多版本機制).
fork()
最著名的COW 的案例就是fork(). 在類Unix 系統中, fork() 是一項系統調用, 可以復制現有的進程再造一個. 當fork() 返回時, 兩個進程將處於相同的進度(打開的文件, 內存等等 相同). 由於COW 機制, fork() 並沒有真的復制了一份進程的內存, 只有父進程改變的數據才在RAM 中復制. 由於大多數使用fork() 的場景中都立刻調用了exec(), 是的整個虛擬內存空間作廢, COW 機制避免了父進程復制的內存完全無用.
另一個副作用是, fork() 用很低的成本創造了進程的私有內存的快照(snapshot). 如果你想在進程的內存中做一些操作, 但又害怕改壞了什麼, 又不想去debug, 盡管fork()
Pages 分頁
虛擬內存被拆分為Pages. 分頁的大小由CPU 規定, 通常為4KiB. 這意味著內核中管理內存的粒度是4K. 當請求內存的時候, 內核返回的將是1 個或多個Pages, 當釋放內存的時候也是以Page 為單位(Oracle 默認數據文件格式8K), 任何封裝良好的API, 例如malloc, 都是在用戶端發起的.
對每一個分配的Page, 內核保持了一組特權(permission): Page 可以被讀, 寫, 或者執行. 這些特權在分配內存的時候, 或者在之後調用mprotect() 的時候被設置. 沒有被分配的Page 不能被存取.
內存類型
不是所有在虛擬內存中分配的內存都是一樣的. 我們可以從兩個方向分類. 第一類是內存是否是私有的或者共享的. 第二類是內存是否是file-backed. 這創造了一個4 個內存種類的分類.
private shared
anonymous
1. stack mmap(ANON, SHARED)
2. malloc()
3. mmap(ANON, PRIVATE)
4. brk()/sbrk()
file-backed 1. mmap(fd, PRIVATE) mmap(fd, SHARED)
2. binary/shared libraries
Private Memory
私有內存是進程獨有的. 大多數程序使用的都是私有內存.
由於私有內存不能被其他進程存取, 所以私有內存服從COW. 副作用是, 即使內存是私有的, 多個進程也會共享一段物理內存去存儲數據. 例如二進制文件和shared libraries.
Shared Memory
共享內存是設計用來實現進程間的通信. 當共享內存被修改, 其他進程可以看到改變.
Ananymous Memory
匿名內存全部在RAM 中. 但是內核不會在真正在RAM 寫入之前分配地址.
File-backed and Swap
如果內存映射是file-backed, 數據是從磁盤上加載. 多數時候, 用的時候才加載. 但也可以控制內核提前加載. 物理內存不夠用的時候, 內核會從RAM 移動一些數據到磁盤. linux 中, swap 是磁盤上一個特殊區域. 由於虛擬內存, swap pages 對進程來說完全是透明的.