🎯 什麼情境該想到我

當你要回答「這支程式的記憶體到底花在哪」「為什麼二進位檔 200MB 卻啟動很快」「為什麼多開 10 個行程記憶體沒有變 10 倍」,或看 /proc/<pid>/mapspmap、RSS 卻讀不懂的時候。

⚙️ 怎麼用

一、先理解演化:粒度決定一切

直接用實體位址有三個病(PDF p39–40):位址空間不隔離、記憶體使用效率低、程式執行的位址不確定。

  • 分段(Segmentation) 解決了第 1、3 個——一整段虛擬空間映射到一塊實體空間,隔離了,也不必重定位。
  • 沒解決第 2 個:映射粒度仍是「整個程式」,記憶體不足時換入換出的是整支程式(p42)。
  • 分頁(Paging)局部性原理把粒度縮到 4KB 才真正解決(p42)。32 位元 4GB 空間按 4KB 分,共 1,048,576 頁。

全書的主線法寶:「计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决」(p33,出處不可考,一說 Butler Lampson,一說 David Wheeler)。虛擬記憶體正是這句話最經典的實作。

二、「裝載」的本質是建立映射,不是搬資料

建立一個行程只做三件事(p182–183):

  1. 建立獨立的虛擬位址空間——在 i386 Linux 上其實只是配一個 page directory,連頁映射關係都先不設
  2. 讀可執行檔頭,建立「虛擬空間 ↔ 可執行檔」的映射關係。這一步才是傳統意義的「裝載」。
  3. 把 CPU 指令暫存器設成入口位址,啟動執行。

做完這三步,程式的指令與資料一個位元組都還沒進記憶體。真正的載入靠 page fault:CPU 摸到空頁 → 硬體捕獲 → OS 查映射關係算出該頁在檔案的哪個偏移 → 配實體頁 → 讀入 → 建映射 → 回到出錯指令重跑。

也因為裝載其實是映射,「可執行檔很多時候又被叫做映像檔(Image)」(p183)。

三、看懂 /proc/<pid>/maps

08048000-080b9000 r-xp 00000000 08:01 2801887  ./SectionMapping.elf
080b9000-080bb000 rw-p 00070000 08:01 2801887  ./SectionMapping.elf
080bb000-080de000 rw-p 00000000 00:00 0        [heap]
bf7ec000-bf802000 rw-p 00000000 00:00 0        [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0        [vdso]

欄位依序是:位址範圍/權限/在映像檔中的偏移/設備主次號/inode/路徑(p191)。

  • 權限的 p = private(COW),s = shared。
  • 設備號與 inode 都是 0 = 匿名虛擬記憶體區域(heap、stack、vdso 都是)。
  • 這張表的單位叫 VMA(Virtual Memory Area)。OS 的基本原則是:把「權限相同 + 來自同一映像檔」的東西合併成一個 VMA(p191)。

四、Section 是給連結器看的,Segment 是給裝載器看的

同一個 ELF 檔的兩種視角(p189):

  • Section → 連結視圖(Linking View)readelf -S
  • Segment → 執行視圖(Execution View)readelf -l

映射以 4096 為單位,一個 ELF 有十幾個 Section,各自映射會產生大量頁內碎片。所以連結器把權限相同的 Section 合併成 Segment,大致三類(p186):可讀可執行(程式碼)、可讀可寫(資料 + BSS)、唯讀(唯讀資料)。一個 Segment 對應一個 VMA,這就是上面 maps 只有兩行檔案映射的原因。

書中的算例很直觀:.text 4097 bytes + .init 512 bytes,分開映射要 3 頁,合併成一個 Segment 只要 2 頁(p186)。

五、順手可查的

  • readelf -lp_memsz > p_filesz多出來的部分自動填 0——那就是 BSS,所以不需要獨立的 BSS Segment。
  • 空間切分:Linux 預設核心 1GB(0xC0000000 以上)+ 使用者 3GB;Windows 預設核心 2GB + 使用者 2GB(p175、p309)。
  • 書中實測 32 位元下 malloc 最大約 Linux 2.9GB / Windows 1.5GB,會受動態庫數量影響(p193)。

🧪 我實際套用的紀錄

  • 2026-08-02:(待填)

⚠️ 注意

  • RSS 高不代表洩漏。 有映像檔的 VMA 是檔案映射(可回收、可跨行程共享),匿名 VMA 才是真的獨佔實體記憶體——看 maps 時先分這兩類。
  • 「程式碼段共享」是分頁機制給的,不是動態連結給的。 多個行程跑同一支程式,唯讀的指令部分記憶體只需一份;書中量測 IE 7.0 總虛存 112,844 KB,其中私有只有 15,944 KB,96,900 KB 是共享的(p85)。
  • 冷啟動慢的成因常常在這裡:page fault 是同步的磁碟 I/O。「檔案大但啟動快」與「跑一陣子 RSS 才慢慢長起來」是同一個機制的兩面。
  • 書中的位址佈局圖是 2.4 核心(共享庫在 0x40000000);2.6 之後共享庫被挪到接近堆疊的高位址,heap 從 BSS 末端到 stack 之間幾乎一馬平川(p332–333)。看老書的圖要留意。

🔗 相關工具