接著我們將從作業系統的角度探討,CPU 如何與周邊設備進行溝通。
根據昨天的架構圖,我們可以看到,CPU 與 RAM 及外部設備的溝通都必須通過 Root Complex。簡單來說,CPU 會給 Root Complex 一個特定目標(地址),然後透過讀寫某些記憶體或數據來進行溝通。在 Intel CPU 架構中,與周邊設備溝通的主要模式有兩種:Memory-Mapped I/O (MMIO) 和 Port-Mapped I/O (PMIO)。今天我們會先介紹 MMIO 模式,接著在明天討論 PMIO 模式。
MMIO(記憶體映射輸入/輸出)顧名思義,就是將周邊設備的記憶體映射到實體記憶體空間 (Physical Address Space),並將其視為一般的記憶體來操作。
物理記憶體地址空間 (Physical Address Space) 是 Root Complex 負責管理的記憶體區域,在物理記憶體地址空間中,除了前面討論的RAM 也包含其他周邊 PCI(e) 設備的記憶體。作業系統在操作虛擬記憶體地址時,地址會經由記憶體管理單元 (MMU) 轉換成對應的物理地址。這些地址最終會被傳送到 Root Complex,根據其映射關係決定是訪問 RAM 還是其他周邊設備。
我們可以使用 cat /proc/iomem
指令來檢視物理記憶體空間的映射關係:
> cat /proc/iomem
00000000-00000fff : Reserved
00001000-0009dfff : System RAM
0009e000-0009efff : Reserved
0009f000-0009ffff : System RAM
000a0000-000fffff : Reserved
000a0000-000bffff : PCI Bus 0000:00
00000000-00000000 : PCI Bus 0000:00
00000000-00000000 : PCI Bus 0000:00
00000000-00000000 : PCI Bus 0000:00
000e0000-000effff : PCI Bus 0000:00
000f0000-000fffff : System ROM
00100000-63e45fff : System RAM
63e46000-63e46fff : Reserved
...
這裡可以看到,物理記憶體空間的部分地址分配給了系統記憶體 (System RAM),其他部分則映射給周邊設備,如 PCI 匯流排 (PCI Bus)。
當驅動程式需要透過 MMIO 操作周邊設備時,它並不會直接訪問物理地址,而是會先更新 Page Table,將其映射到虛擬地址空間中,然後使用該虛擬地址進行操作。
// arch/x86/mm/ioremap.c
void __iomem *ioremap(resource_size_t phys_addr, unsigned long size)
Linux Kernel 提供 ioremap
函數,允許驅動程式配置記憶體映射。該函數會將輸入的設備實體記憶體地址和大小,映射到一個虛擬記憶體區域,並返回對應的虛擬地址。
那麼,作業系統跟 Root Complex 如何決定周邊設備的記憶體應該映射到物理記憶體空間的哪個位置呢? 等明天我們介紹完另外一種CPU與周邊設備的溝通方式 Port-Mapped I/O (PMIO) 後,會再來介紹。