操作系統是如何一步步發明虛擬內存的?
那時引以為傲的System/360大型機雖然配備了豪華的256KB物理內存(價格相當于今天的數百萬美元),但在引入多進程后內存相關的問題開始出現,因為多個進程可以同時運行在內存中。
你面臨的核心問題是:如何保證多進程能夠高效共享有限的物理內存?
最初的嘗試:固定分區
你的第一個嘗試是最直觀的方法,將物理內存劃分為幾個固定大小的區域,每個區域分配給一個程序:
圖片
這就是所謂的固定分區(Fixed Partitioning),這個想法很簡單,你很快實現了這個機制:
// 固定分區內存管理的簡單實現
struct memory_partition {
void* start_address; // 分區起始地址
size_t size; // 分區大小
bool is_occupied; // 是否被占用
int process_id; // 占用進程ID
};
這個簡單的分區系統確實解決了一些問題。它允許多個程序同時駐留在內存中,并提供了基本的內存隔離。然而,它很快就暴露出了嚴重的缺陷。
問題出在內存利用率上:一個只需要10KB內存的小程序占用了整個64KB的分區,而一個需要70KB的程序卻無法運行,因為沒有任何一個分區足夠大,盡管系統中空閑內存超過了70KB!
你意識到,固定分區雖然簡單,但極其浪費內存資源。
它無法適應程序大小的變化,也無法解決運行大型程序的問題。
這個方案本質就是吃大鍋飯,不管你可執行程序本身有多大都給你固定內存,打破大鍋飯的最佳方法就是按勞分配。
動態分區:按需分配
既然是按勞分配那就不能預先劃分內存,而是根據程序的實際需求動態分配內存塊,用多少給多少:
// 動態分區內存管理
struct memory_block {
void* start_address; // 內存塊起始地址
size_t size; // 內存塊大小
bool is_free; // 是否空閑
struct memory_block* next; // 鏈表中的下一個塊
};
struct memory_block* free_list; // 空閑內存塊鏈表
這就是你在數據結構課上學到的鏈表。
動態分區確實提高了內存利用率,程序可以獲得剛好滿足其需求的內存量,這種內存分配方法開始流行起來。
然而,隨著系統運行時間的增長,大量用戶開始反饋物理內存很快耗盡導致程序崩潰,一通debug后你發現了問題:內存碎片。
只需要幾周的運行,系統中就會出現了大量的小內存塊,它們分散在各處,雖然總和足夠大,但沒有一個連續的塊能滿足新程序的需求。
更糟糕的是,即使使用動態分區,仍然無法運行那些需要超過物理內存總量的程序。
因為在20世紀60-80年代,雖然計算機物理內存有限(如KB級別),但程序規模卻在逐漸增大(如大型科學計算、數據庫系統),這是一個根本性的限制,你需要一種全新的思路...
覆蓋技術:程序員的自我管理
面對內存不足的問題,你開始思考,既然內存一次性裝不下大型程序,那么為什么不把這個大型程序拆開了、用到哪些就裝哪些呢?
看上去好像能解決問題,你進一步思考,程序其實可以被劃分為多個獨立的功能模塊,一些核心的模塊可能需要始終駐留在內存(如主控制邏輯、核心函數),而非核心的功能模塊可以按需動態加載到共享內存區域,覆蓋前一個模塊。
假設可執行程序A劃分為一個核心模塊和4個功能模塊,那么當需要運行模塊1時就把模塊1加載到共享內存區域,當需要運行模塊2時就把模塊2加載到共享內存中覆蓋掉原來的模塊1:
圖片
這樣就能實現在有限的物理內存中運行超大程序的目的,這就是早期操作系統中的"覆蓋技術"(Overlay)。
這種方法要求程序員手動將程序分割成多個模塊,并在運行時根據需要將不同模塊加載到同一塊內存區域。
// 程序員使用覆蓋技術的偽代碼
void main() {
// 主模塊始終在內存中
// 需要模塊A時
load_module("module_A", OVERLAY_REGION);
execute_module_A();
// 需要模塊B時,覆蓋同一內存區域
load_module("module_B", OVERLAY_REGION);
execute_module_B();
// 再次需要模塊A時
load_module("module_A", OVERLAY_REGION);
execute_module_A_again();
}
覆蓋技術確實突破了物理內存限制,可以在有限的物理內存上運行大型程序,是一種非常聰明的方法。
但它有嚴重的缺點:
- 程序員必須手動管理內存,這極其復雜且容易出錯
- 程序必須預先知道哪些模塊可以共享內存區域
- 頻繁的模塊加載會導致性能下降
這讓你開始認識到:內存管理太重要了,絕不能完全依賴程序員自己手動管理。
因此你需要一個系統級的解決方案,能夠自動管理內存,對程序員透明,同時允許程序使用超過物理內存的地址空間,這就是后來的虛擬內存技術。