今天從台南火車站出站時,好像撇到前任(我發誓我也不確定是不是他
因為完全是在意料之外,大家就很快速的走向各自的方向,但就是那個臉旁。整個害我心揪了一下...
這個女孩很突然的出現在我的記憶裡,又很突然的離開...
真的都會想到,網路上的一個話題,一個人在你生命中短暫的出現,是獎勵還是懲罰...

基本概念
虛擬記憶體(Virtual Memory, VM)是現代作業系統的核心技術之一,它在硬體(特別是記憶體管理單元,MMU)與軟體的協同下,為每個程式創造了一個獨立、連續、完整的記憶體視圖,這個視圖被稱為虛擬地址空間(Virtual Address Space, VAS)。這項技術從根本上解耦了程式所認為的記憶體位址(虛擬位址)與資料實際存放的記憶體位址(實體位址)。程式員可以不再擔心實體記憶體容量的限制,而作業系統則能更有效地管理記憶體資源。
虛擬記憶體的核心優勢
- 突破實體記憶體限制:即使實體記憶體只有 8 GB,程式也能請求並使用高達 16 GB 的虛擬記憶體空間。作業系統會將程式的部分內容存放在硬碟上,只在需要時才載入到實體記憶體,這讓你可以執行比實體記憶體更大的程式,也簡化了大型程式的設計。
- 提高多工效率:在傳統系統中,一個程式必須完全載入實體記憶體才能執行。有了虛擬記憶體,程式只需要將最常用的一小部分載入記憶體即可開始運行,這使得系統能夠同時容納更多程式,從而提高 CPU 的使用率和整體系統吞吐量。
- 減少 I/O 開銷:虛擬記憶體使用了需求分頁(Demand Paging)的技術,只在程式真正需要某個頁面時,才從硬碟將它載入。這避免了在程式啟動時一次性載入所有程式碼與資料,顯著減少了啟動時間和不必要的 I/O 操作。
- 提供程式隔離與保護:每個程式都擁有自己獨立的虛擬地址空間,這個空間是私有的。一個程式無法直接存取另一個程式的記憶體,除非雙方明確設定為共享。這種隔離機制防止了一個程式的錯誤(例如越界存取)影響到其他程式或作業系統本身,大大增強了系統的穩定性與安全性。
虛擬地址空間(Virtual Address Space, VAS)
每個程式「看到」的記憶體是虛擬化後的連續空間,作業系統與硬體(MMU:Memory Management Unit)負責將其映射到實際的物理記憶體。以虛擬地址空間區段分布範例(以 32-bit 為例):
區段名稱 |
功能 |
Text(程式碼區) |
儲存可執行指令,通常為唯讀區域,避免程式被隨意改動。 |
Data(全域變數區) |
儲存初始化過的全域變數、靜態變數。 |
Heap(堆積區) |
動態配置記憶體空間,例如 malloc() 申請的區域。 |
Stack(堆疊區) |
儲存函式呼叫、區域變數、回傳位址等,隨函式呼叫增減。 |
Memory-Mapped區域 |
包含共享函式庫、檔案映射、共享記憶體區段等。 |

虛擬記憶體中的共享機制
虛擬記憶體不僅提供隔離,也能讓多個程式安全地共享資源,這是它高效能的關鍵。
- 共享程式庫:最經典的例子是 libc(C 標準函式庫)。當多個程式都使用 printf() 函式時,這段程式碼並不需要被複製多次。作業系統只需要將 libc 載入到實體記憶體一次,然後讓所有需要它的程式,其各自的頁表都將虛擬地址空間中對應的頁面,映射到實體記憶體中的同一個 libc 頁框。
- 共享記憶體區段:這是一種程式間通訊(IPC)的方式。一個程式可以透過系統呼叫(如 mmap())建立一個共享記憶體區段,並將它映射到自己的虛擬地址空間。其他程式也能將這個相同的實體區段映射到自己的虛擬空間中。如此一來,多個程式就可以讀寫同一塊記憶體,實現高效的資料交換。
- Copy-on-Write(COW):這是一種優化技術,常見於 fork() 系統呼叫。當父行程呼叫 fork() 建立子行程時,作業系統並不會立即複製父行程的所有記憶體頁面。相反,父子行程最初共享所有頁面,但這些頁面都被標記為只讀。只有當其中一個行程試圖寫入某個共享頁面時,才會觸發一個陷阱,作業系統這時才為該頁面創建一個獨立的副本。這極大地加速了新行程的建立,並節省了記憶體。

運作原理總結
虛擬記憶體技術透過硬體與軟體的緊密協作,實現了以下核心邏輯:
- 程式執行時,產生虛擬位址。
- MMU 截獲虛擬位址,並透過分頁表將其轉換為實體位址。
- 如果 MMU 在分頁表中發現該虛擬頁面不存在於實體記憶體中,便會發出分頁錯誤(Page Fault)。
- 作業系統接收到分頁錯誤後,會暫停該程式,從硬碟尋找對應的頁面,並將其載入到實體記憶體中一個可用的頁框。
- 更新頁表後,作業系統讓程式從剛剛產生錯誤的指令處重新執行。
需求分頁(Demand Paging)
需求分頁是虛擬記憶體技術的核心,它讓作業系統能夠突破實體記憶體的限制,並極大地提高了系統的多工能力。它的核心思想是「只在需要時才載入(Lazy Loading)」。
傳統的分頁方式,會在程式啟動時,一次性將所有程式碼和資料都載入到實體記憶體中,這不僅浪費記憶體,也造成啟動時間過長。而需求分頁則完全相反:程式啟動時,作業系統只會載入一小部分必要的頁面(例如主程式的啟動程式碼),其他頁面則會被標記為「無效(Invalid)」,存放在硬碟上。
當程式執行到某個點,需要存取一個尚未載入的頁面時,這時才會觸發分頁錯誤(Page Fault),由作業系統負責將該頁面從硬碟載入到實體記憶體中。

分頁錯誤處理流程(Page Fault Handling)
此機制稱為 pure demand paging:初始時不載入任何頁,完全依需求加載。
- 當程式試圖存取尚未載入的頁時,會觸發 Page Fault(分頁錯誤),由作業系統處理:
- 陷入(trap)作業系統。
- 作業系統檢查該記憶體存取是否合法。
- 若非法 → 終止程序。
- 若合法但尚未載入 → 繼續以下步驟。
- 從「自由框架清單(Free Frame List)」中找出一個空閒 frame。
- 從磁碟(通常是 swap 空間或檔案系統)載入該頁。
- 更新頁表與內部表格(將 invalid 改成 valid)。
- 重新執行原本中斷的指令(可從同一位置繼續執行)。

指令重啟挑戰(Instruction Restart Challenge)
分頁錯誤處理的一個關鍵挑戰,在於如何處理在執行中途被中斷的指令。有些複雜的指令(例如 Intel x86 架構中的 MVC 指令,用於移動大量資料)可能在執行過程中需要存取多個記憶體位址。如果中途發生分頁錯誤,程式的狀態可能已經被部分改變,直接重新執行可能會導致錯誤。
為了解決這個問題,作業系統和硬體通常會採取以下策略:
- 原子性操作:設計 MMU 在發生分頁錯誤時,能夠精確地回溯到指令執行前的狀態。例如,將暫存器和記憶體狀態恢復到中斷點之前的快照,確保重新執行時不會有任何副作用。
- 軟體處理:在較簡單的架構上,作業系統可以透過分析中斷點,來判斷如何從正確的位置重新開始。這可能涉及儲存中間結果,或是重新執行一整個指令,但這要求指令必須是可中斷且可重新執行的。
空框架清單(Free-Frame List)與頁面替換(Page Replacement)
當系統的實體記憶體幾乎被佔滿,而此時又發生分頁錯誤時,空框架清單(Free-Frame List)中可能就沒有可用的框架了。這時,系統必須進入頁面替換(Page Replacement)階段。
頁面替換是一種策略,作業系統會從實體記憶體中挑選一個「犧牲者(Victim Page)」,將其內容寫回硬碟,騰出空間給需要載入的新頁面。這個過程通常涉及:
- 選擇犧牲者:使用頁面替換演算法(如 FIFO、LRU、Optimal 等)來選擇一個最適合被換出的頁面,目標是減少後續的分頁錯誤。
- 寫回硬碟:如果被選中的頁面自從載入後曾被修改過(即其「修改位元(Dirty Bit)」為 1),其內容必須被寫回硬碟。如果未被修改,則可以直接丟棄。
- 更新頁表:將犧牲頁面的頁表項目標記為無效。
- 載入新頁:將新的頁面載入到這個被騰出的框架中。
這個機制確保了系統即使在實體記憶體不足的情況下,依然能夠穩定運行,雖然代價是效能會因為頻繁的磁碟 I/O 而下降,這就是所謂的顛簸(Thrashing)。

Page Replacement(頁面替換)
在需求分頁(Demand Paging)的機制下,當一個程式執行中發生分頁錯誤(Page Fault),需要從硬碟載入新頁面時,若主記憶體中已經沒有可用的空框架(Free Frame),作業系統就必須執行頁面替換(Page Replacement)。
這個過程就像是一個「記憶體清道夫」,它會從主記憶體中選擇一個頁面(Page),將其踢出,以騰出空間來載入新的頁面。這個被選中的頁面被稱為「受害者頁面(Victim Page)」。
頁面替換策略的優劣直接影響著系統的效能。如果選擇了錯誤的頁面,很可能在不久的將來又會再次發生分頁錯誤,形成惡性循環,這種現象被稱為顛簸(Thrashing),會導致系統效能急劇下降,因為大部分時間都在進行頻繁的磁碟 I/O。
頁面替換的處理流程
- Page Fault 發生:程式請求一個不在記憶體中的頁面。
- 查詢二級儲存(Secondary Storage):找到該頁面在硬碟(Swap Space 或檔案)的位址。
- 檢查 Free Frame List
- 如果有空的 Frame → 直接載入,不需替換。
- 如果沒有空 Frame → 進入頁面替換程序。
- 選擇 Victim Page(受害者頁面):使用 頁面替換演算法 決定哪一頁要被踢出。
- 檢查 Dirty Bit(修改位元)
- 如果該頁 Dirty Bit=1,代表內容已被修改 → 必須先寫回硬碟,再釋放。
- 如果 Dirty Bit=0,該頁與硬碟一致 → 可直接捨棄。
- 載入新頁面至 Victim Frame:更新頁表內容,將新頁面標記為 Valid。
- 恢復被中斷的指令,程式繼續執行。


修改位元(Dirty Bit)的關鍵作用
修改位元是分頁系統中一個簡單但極其重要的優化機制。它直接關係到系統的效能,尤其是在寫入操作頻繁的應用中。
- 減少不必要的 I/O:磁碟 I/O 是所有電腦操作中速度最慢的環節之一。如果每個被替換的頁面都必須寫回硬碟,即使內容沒有改變,也會造成巨大的效能損失。Dirty Bit 確保只有那些真正被修改過的頁面才需要寫回硬碟,從而顯著降低了磁碟 I/O 的頻率,提升了整體系統響應速度。
- 區分讀取和寫入:在頁面替換演算法中,Dirty Bit 也可以被用來作為選擇受害者頁面的依據之一。例如,在替換策略中,系統可能會優先選擇那些 Dirty Bit = 0 的頁面,因為它們可以更快速地被替換出去,無需等待磁碟寫入。