接下來,就是要來談記憶體相關的部分了~
在電腦系統中,記憶體(Memory) 是運作的核心之一。CPU 必須從記憶體中取得指令與資料,才能執行程式。CPU 的基本執行流程如下:
於 CPU 只能直接操作暫存器(Registers)與主記憶體(Main Memory),而不能直接操作速度較慢的硬碟。為了彌補 CPU 與主記憶體之間巨大的速度差異,快取(Cache)便應運而生。快取是一種容量較小但速度極快的記憶體,它會儲存經常被 CPU 存取的主記憶體資料副本。當 CPU 需要資料時,會先從快取中尋找。如果資料在快取中,就能快速讀取,大幅提升效率。如果快取中沒有,才會去主記憶體抓取。
現代作業系統運用虛擬記憶體 (Virtual Memory) 與多工 (Multitasking) 機制,讓每個程式都感覺像是「擁有一整塊自己的連續記憶體空間」,但實際上,所有的程式都共用主記憶體。因此,系統必須將程式「看到的位址」與「實際硬體記憶體的位址」分開處理,並由作業系統負責進行轉換與保護。
而 MMU (Memory Management Unit) 就是一個硬體元件,它負責在程式執行過程中,即時 (on the fly) 將邏輯位址轉換為實體位址。這個轉換過程可以簡單地表示:實體位址=邏輯位址+基底位址
其中,「基底位址 (Base Address)」是由作業系統為每個程式指定的起始位址。
在多工系統中,許多程式 (processes) 同時在主記憶體中執行。為了確保系統穩定與安全,作業系統必須實現以下保護機制:
為了解決這些問題,作業系統採用了基底暫存器 (Base Register) 和限制暫存器 (Limit Register) 的方式來實施保護。每個程式的記憶體使用範圍都被精確地限制在一個區塊內:
當使用者程式提出一個邏輯位址 (Logical Address) 進行存取時,硬體會自動檢查位址是否合法:硬體會先檢查這個邏輯位址是否超過限制暫存器所設定的大小。如果 logical_address > limit,代表程式試圖存取超出其允許範圍的記憶體。這將觸發一個陷阱 (trap),將控制權交給作業系統處理,通常會終止該程式。
physical_address = base + logical_address
// 系統檢查是否有違規:若 logical_address > limit → 觸發 trap → 切入 OS 處理
舉例而言:
base = 1000,limit = 500
使用者提出 logical_address = 400
系統計算:physical_address = 1000 + 400 = 1400 ✅
若提出 logical_address = 600 → 超過 limit → ❌ 非法存取
當我們撰寫程式時,變數或指令的位址都是抽象的符號位址 (symbolic address),例如 x = y + 3。此時,我們並不知道 x 和 y 在記憶體中的實際位置。位址繫結 (Address Binding) 就是將這些符號位址轉換為實際的實體位址 (physical address) 的過程。這個轉換可以發生在不同的時間點,主要有三種:
在程式被編譯成執行檔時,編譯器會為程式碼中的每個符號位址分配固定的實體位址。例如,程式碼會被指定從主記憶體的位址 1000 開始載入。
優點:簡單、快速。
缺點:彈性極低。如果程式需要載入到不同的記憶體位置,例如從位址 2000 開始,那麼程式必須重新編譯。這在多工環境下幾乎不可行。
在程式被載入到記憶體時,編譯器會產生可重定位 (relocatable) 的程式碼。這意味著程式碼中的位址不是固定的,而是相對位址。當程式載入器 (loader) 將程式載入到主記憶體時,會根據實際載入的起始位置,動態地調整所有的位址。
優點:比編譯時繫結更具彈性,程式可以在記憶體中移動,只要在載入時決定最終位置即可。
缺點:一旦程式載入完成,其位址便固定了,之後便無法在記憶體中移動。
這是現今最常見且最靈活的位址繫結方式。在程式執行過程中。程式碼中的位址在執行過程中會不斷地被轉換。
這個機制需要硬體支援,例如 記憶體管理單元 (MMU)。MMU 會即時將程式產生的邏輯位址 (logical address) 轉換為實體位址 (physical address)。
優點:極度靈活。程式在執行過程中可以被搬動到不同的記憶體位置,例如從磁碟載入,或在記憶體中進行區塊移動,這對虛擬記憶體和分頁 (paging) 等技術至關重要。
缺點:需要額外的硬體支援和軟體開銷。
在早期的作業系統中,當你執行一個程式時,整個程式(包含主程式、所有函式以及所需的函式庫)都會一次性地全部載入到記憶體中。這種方法的主要缺點是記憶體使用量高,因為許多不常使用甚至從未被呼叫的程式碼也佔據了寶貴的記憶體空間。為了解決這個問題,動態載入 (Dynamic Loading) 與動態連結 (Dynamic Linking) 的設計應運而生。
動態載入的核心概念是:只在「需要用到某個函式時」才將其載入到記憶體中,否則就先不載入。
在初始階段:當一個程式開始執行時,作業系統只會載入主程式本身。
在執行階段:當程式碼執行到一個需要呼叫特定函式(例如 load())的位置時,載入器才會把這個函式從磁碟載入到記憶體中。
這種方式的優點顯而易見:
動態連結是動態載入概念的延伸,主要解決的是函式庫 (libraries) 的重複載入問題。
這種機制的主要優點是: