如果我們將兩個4G內存插入內存插槽,得到的內存地址空間是0到8G嗎?是不是0到4G是第一根內存,4到8G是第二根內存呢?實際情況相差甚遠,內存在物理地址空間的映射是分散的。一部分原因是4G以下有Memory map IO(mmio)空間和PCIe的配置空間,另一個原因是Interleaving會打撒內存地址到各個Channel、DIMM甚至是Rank和bank上。今天我們就一起來了解一下x86系統的地址空間分布。
物理地址空間
一個典型的物理地址空間是這樣的:
其中只有灰色部分是真正的內存,其余都是MMIO。而內存被分為High DRAM和Low DRAM,如圖:
為什么要把內存強行分割成兩塊呢?因為歷史的包袱。最早內存都很小,32位的地址(4G)空間看起來永遠也用不完,低地址被分配給內存用,高地址就自然而然被分配用來給Memory map IO。既然已經分給它們了,為了兼容以前的驅動,這一塊就被固定下來。再有內存就只能從4G以上分配了。
Low MMIO和High MMIO
Low MMIO結構如下圖:
其中有幾塊要特別說明一下:
1.Boot Vector的空間是BIOS內容映射的地址,它的大小是可以調節的,為了滿足不同大小的BIOS。
2.Local APIC是APIC中斷模式各個內核local APIC寄存器的映射地址。
3.PCI ECAM也有叫做PCIBAR,是PCIe配置地址空間的映射地址。它的起始地址可調,臺式機BIOS一般會把它設置得很高,這樣4G以下內存會比較大,方便32位Windows使用。舉個例子,如果我們把PCIe BAR(BEGREG)設為0x80000000,那么盡管插了8G DIMM,4G以下也不會超過2G的內存可以使用,而2到8G的真實內存都被映射到在4G地址空間以上了,而這些是32位Windows使用不了的。所以有的主板運行32位操作系統發現可用內存小了一大塊就是這個原因。它的大小可以修改,一般可以設為64MB和128MB。
High MMIO被BIOS保留作為64位mmio分配之用,例如PCIe的64位BAR等。
Low DRAM和High DRAM
4G以下內存最高地址叫做BMBOUND,也有叫做Top of Low Usable DRAM (TOLUD) 。BIOS也并不是把這些都報告給操作系統,而是要在里面劃分出一部分給核顯、ME和SMM等功能:
紅框中是在low DRAM被“偷”的部分
4G以上的內存最高端叫做Top of Up Usable DRAM (TOUUD) ,再上面就是High MMIO了。
1MB以下比較特殊,里面全部都是已經被淘汰的傳統BIOS和DOS關心的內容,我們叫它DOS Space或者Legacy Region:
在那里,我們習慣用傳統的實模式地址來劃分它們的具體內容:
1.0~640KB,傳統DOS空間。
2.A段和B段,傳統SMM空間。VGA的MMIO也被映射到這里,可以通過寄存器切換。
3.C段和D段,legacy opROM映射空間和EBDA空間。
4.E段和F段,BIOS空間的Lower和Upper映射地址。BIOS的rom內容也會被映射到這里,方便Legacy BIOS實模式跳轉到保護模式。
內存的Interleave
從前面可以看出內存在地址空間上被拆分成兩塊:Low DRAM和High DRAM。那么在每塊地址空間上分配連續嗎?現代內存系統在引入多通道后,為了規避數據的局部性(這也是Cache為什么起作用的原因)對多通道性能的影響,BIOS基本缺省全部開啟了Interleaving,過去美好的DIMM 0和DIMM 1挨個連續分配的日子一去不復返了。
什么是Interleaving?簡單來說,就是讓內存交錯起來,如下面的動圖:
這是一個bank層級的模4的interleaving。在桌面電腦上,常見的還有Channel級的、DIMM級的和Rank級的。
服務器上Interleaving更是不可或缺,它的粒度更細,可以達到數十bytes層級的interleave,它和內存的其他特性,如類似磁盤陣列RAID的內存spare, mirror特性,構成了復雜異常的內存映射系統。在BIOS里面,臺式機/筆記本內存映射相對簡單,只有一個大表和數十個寄存器;而在服務器BIOS中,有數個相互關聯的大表和寄存器陣列來解碼(decode)內存的請求,代碼的硬件邏輯也是相當復雜。關于它,我會有一篇專欄文章討論地址譯碼和地址反向解碼,詳細內容那里再說,這里只需要知道,物理內存分布在各個DIMM上就夠了。
物理地址到內存單元的反推
BIOS實際上一手導演的內存的分配,它當然可以從任何物理地址反推回內存的單元地址。我們可以用下面一組數據來唯一確定某個內存單元:
Channel #;DIMM #; Rank #;Bank #;Row #;Column #
在內存分配表缺失的情況下,BIOS甚至可以通過它填過的寄存器重建這個映射表。但實際上BIOS并不希望一般用戶知道這些信息,因為有安全性問題。
暴露內存信息容易招來內存側信道攻擊(Side Channel),比較有名的有Row hammer攻擊。簡單的來說它是通過反復寫某個內存單元,借助內存的特性,希望影響相鄰Row/Column的內容。
有些情況確實需要知道這些信息,就是內存出錯的時候。和大家想象的不同,內存是會出錯的。尤其云服務器中內存的出錯是十分頻繁的。出錯起來也千奇百怪,開始可能是偶爾的隨機錯誤,經過ECC等校正后,就再也不會復現;而有時是某個Bit總是出錯,進而慢慢的整個row、column或者相鄰的cell開始出錯,從可以糾正的錯誤變成不可修正的錯誤,導致服務器必須停機。這時候就必須知道哪個內存壞了,進而換掉它。
報告給操作系統,但這個信息里面只有物理地址,如何才能知道是哪個內存單元壞了呢?在Linux上面可以通過edca(參考資料4),有編程經驗的同學可以通過edca的程序接口(參考資料3),可以得到更加豐富的信息。
如何關掉Interleaving
對內存有特殊需求的朋友,如果希望內存連續,可以在BIOS里面關閉所有的Interleaving來達成這個目標:
注意是所有的。之后可以通過SMBIOS來看到內存分布信息(dmidecode)。
結論
BIOS作為內存的大管家,也負責內存的分配和映射memory map。它會把這些信息通過E820, GetMemoryMap函數和SMBIOS傳遞給操作系統。操作系統在此基礎上再建立頁表,產生虛擬地址。
-
寄存器
+關注
關注
31文章
5336瀏覽量
120232 -
Linux系統
+關注
關注
4文章
593瀏覽量
27392 -
DRAM芯片
+關注
關注
1文章
84瀏覽量
18011 -
PCIe接口
+關注
關注
0文章
120瀏覽量
9702 -
DOS系統
+關注
關注
0文章
9瀏覽量
1394
發布評論請先 登錄
相關推薦
評論