1. 程式人生 > >e820與kernel實體記憶體對映

e820與kernel實體記憶體對映

http://deltamaster.is-programmer.com/posts/37297.html

我們都對作業系統如何管理記憶體有一定的瞭解,然而,在作業系統開始管理記憶體之前,首先要獲取實體記憶體的資訊,比如一共有多少實體地址是可用的,有哪些實體地址是被ACPI(Advanced Configuration and Power Interface)資料使用,這些資訊從何而來呢?

e820就是BIOS像x86架構(包括x86_64)上的作業系統載入程式提供實體記憶體資訊的功能。當請求BIOS中斷號15H,並且置操作碼AX=E820H的時候,BIOS就會向呼叫者報告可用的實體地址區間等資訊,e820由此得名。

Linux核心也通過這種機制來獲得實體地址資訊,使用dmesg可以看到相關的資訊:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

[    0.000000] BIOS-provided physical RAM map:

[    0.000000]  BIOS-e820: 0000000000000000 - 000000000009e800 (usable)

[    0.000000]  BIOS-e820: 000000000009e800 - 00000000000a0000 (reserved)

[    0.000000]  BIOS-e820: 00000000000e0000 - 0000000000100000 (reserved)

[    0.000000]  BIOS-e820: 0000000000100000 - 0000000020000000 (usable)  #511MB

[    0.000000]  BIOS-e820: 0000000020000000 - 0000000020200000 (reserved)

[    0.000000]  BIOS-e820: 0000000020200000 - 0000000040000000 (usable)  #510MB

[    0.000000]  BIOS-e820: 0000000040000000 - 0000000040200000 (reserved)

[    0.000000]  BIOS-e820: 0000000040200000 - 00000000aac0d000 (usable)  #1706MB

[    0.000000]  BIOS-e820: 00000000aac0d000 - 00000000aad8e000 (reserved)

[    0.000000]  BIOS-e820: 00000000aad8e000 - 00000000aad95000 (usable)

[    0.000000]  BIOS-e820: 00000000aad95000 - 00000000aad96000 (reserved)

[    0.000000]  BIOS-e820: 00000000aad96000 - 00000000aad97000 (usable)

[    0.000000]  BIOS-e820: 00000000aad97000 - 00000000aadb8000 (reserved)

[    0.000000]  BIOS-e820: 00000000aadb8000 - 00000000aadc6000 (usable)

[    0.000000]  BIOS-e820: 00000000aadc6000 - 00000000aade8000 (reserved)

[    0.000000]  BIOS-e820: 00000000aade8000 - 00000000aaf23000 (usable)

[    0.000000]  BIOS-e820: 00000000aaf23000 - 00000000aafe8000 (ACPI NVS)

[    0.000000]  BIOS-e820: 00000000aafe8000 - 00000000aaffd000 (usable)

[    0.000000]  BIOS-e820: 00000000aaffd000 - 00000000ab000000 (ACPI data)

[    0.000000]  BIOS-e820: 00000000ab000000 - 00000000b0000000 (reserved)

[    0.000000]  BIOS-e820: 00000000e0000000 - 00000000e4000000 (reserved)

[    0.000000]  BIOS-e820: 00000000fec00000 - 00000000fec01000 (reserved)

[    0.000000]  BIOS-e820: 00000000fed10000 - 00000000fed14000 (reserved)

[    0.000000]  BIOS-e820: 00000000fed18000 - 00000000fed1a000 (reserved)

[    0.000000]  BIOS-e820: 00000000fed1c000 - 00000000fed20000 (reserved)

[    0.000000]  BIOS-e820: 00000000fee00000 - 00000000fee01000 (reserved)

[    0.000000]  BIOS-e820: 00000000ff980000 - 00000000ffc00000 (reserved)

[    0.000000]  BIOS-e820: 00000000ffd80000 - 0000000100000000 (reserved)

[    0.000000]  BIOS-e820: 0000000100000000 - 000000014f800000 (usable)  #1272MB

上面是我在自己計算機上得到的資料,其中usable的區間就是實際被對映到實體記憶體上的地址空間,上面標註出來的四個區間,就是我主要的四個可用的實體地址區間了,大約4GB。

  • Usable:已經被對映到實體記憶體的實體地址。
  • Reserved:這些區間是沒有被對映到任何地方,不能當作RAM來使用,但是kernel可以決定將這些區間對映到其他地方,比如PCI裝置。通過檢查/proc/iomem這個虛擬檔案,就可以知道這些reserved的空間,是如何進一步分配給不同的裝置來使用了。
  • ACPI data:對映到用來存放ACPI資料的RAM空間,作業系統應該將ACPI Table讀入到這個區間內。
  • ACPI NVS:對映到用來存放ACPI資料的非易失性儲存空間,作業系統不能使用。
  • Unusable:表示檢測到發生錯誤的實體記憶體。這個在上面例子裡沒有,因為比較少見。

核心讀到這些資訊後,將其儲存在e820map結構體中,有兩個副本,一個符號名叫e820,還有一個符號名叫e820_saved。具體的資料結構可以參考arch/x86/include/asm/e820.h。隨著核心的啟動,核心還會修改e820的資訊。

?

1

2

3

[    0.000000] e820 update range: 0000000000000000 - 0000000000010000 (usable) ==> (reserved)

[    0.000000] e820 remove range: 00000000000a0000 - 0000000000100000 (usable)

[    0.000000] e820 update range: 00000000ab000000 - 0000000100000000 (usable) ==> (reserved)

比如在我的系統上發生了這樣三次e820的改動,這些改動已經與BIOS沒有任何關係了,只是核心自己通過修改自己的記憶體資料結構,來改變自己對記憶體區間的使用。

還有一個典型的例子是當核心啟動引數中有置頂memmap這樣的引數項時,核心會修改指定的usable的區間為reserved,這樣核心就不能將虛擬地址對映到這些實體地址空間了,換言之就是不能使用這塊實體記憶體了(其實通過ioremap還是可以將其對映到核心的虛擬地址空間的,但是這些行為都是自己控制的而不會受到其他程式的干擾)。這樣當我們需要自己管理某段實體記憶體而不希望核心干預時就很有用處,比如基於實體記憶體的檔案系統,就可以這樣實現。

核心中還提供了一系列操作e820資料結構的函式,函式宣告都在arch/x86/include/asm/e820.h,相應的定義都在arch/x86/kernel/e820.c中。文章開頭看到的這段資訊,就是由void __init e820_print_map(char *who)來列印的。

在這個過程中,e820_saved始終保持原始的狀態不變,以便查詢BIOS提供的真實對映資訊,不過核心目前好像沒有使用這個資料結構。我曾經有使用過它,用來檢查某個實體地址是否確實是實體記憶體。