操作系統-內存讀取[半原創]

文章大部分表述圖片來自 : https://www.jeanleo.com/2021/07/06/linux內存管理剖析/ 。 非原創

內存是如何給讀取的

    計算機上電啓動的時候,BIOS會檢測並計算物理內存大小。比方說現在通用的內存都是DIMM針腳插槽類型的,它的PIN針腳有兩百多個,各個針腳各有自己的定義,BIOS就是通過對不同針腳的高低電平設置,由內存反饋其規格信息給BIOS,然後BIOS計算出容量。大概原理就這樣了。但是我們重點是操作系統需要感知主機的內存空間,它是怎麼知道的呢?它是通過BIOS提供的接口去詢問出來的。這個接口就是0x15中斷,其中參數重點參數是ax寄存器中需要設置值e820。然後通過intcall(0x15, &ireg, &oreg)中斷調用,由BIOS通過oreg.di出參將內存信息返回回來。該實現在/arch/x86/boot/memory.c中的detect_memory,由於代碼出參oreg.di也是ireg.di傳進去的值,所以代碼裏面直接讀了buf空間內存。由於每調用一次intcall只會返回一條內存數據信息,所以會循環調用多次才能夠探明整個內存空間。

1297993-20211114115224453-86847209.png

也就是說 操作系統 --> BIOS中斷 中斷來感知內存空間的,這個0x15是一箇中斷號 ,定義在中斷表述表,而這個中斷表又是從哪裏來的呢?我們在上篇文章 裏寫道加載 BIOS 的環節會加載中斷表。

    計算機剛開機的時候只有1M的內存可以使用。 此時內存會被各種外設瓜分,映射在內存的相應區域。同理,BIOS裏的信息會被映射到內存的一段連續的區域中(0xC0000–0xFFFFF),其中最爲關鍵的系統BIOS被映射到了0xF0000–0xFFFFF位置,CPU開機就是執行了系統BIOS這塊內存區域中的代碼,注意BIOS中的程序還會佔用內存開頭的一些區域,如把中斷向量表寫在了內存開始的位置。

    在開機的一瞬間,CPU中的PC寄存器被強制初始化爲0xFFFF0。BIOS程序的入口地址就是0xFFFF0,然後CPU就開始跑起來,執行BIOS中的程序。

中斷0x15有三個子功能,子功能號放到EAX或者AX中,其簡要說明:

  • EAX = 0xE820 遍歷所有內存
  • AX = 0xE801 分別檢測低 15 MB 和 16 MB ~ 4 GB的內存,最大支持 4 GB。
  • AX = 0x88 最簡單的子功能,只能檢測64MB的內存,若超過也只返回 64 MB。

具體的子功能講解見這篇文章

內存由誰來讀取

    內存探測必然是kernel嗎?答案是否定的。先說一下kernel的binary文件吧,它通常放在/boot/下面,名字通常命名爲vmlinuz。這個文件是由setup.bin和vmlinux構造而成,其中vmlinux又由kernel編譯目錄arch/x86/boot/compressed下的cmdline.o、head.o、kaslr.o等連同壓縮後的vmlinux.bin.gz合併構成。其中檢測內存的detect_memory()函數就是在setup.bin裏面,但是這僅限於Grub legacy(即Grup 0.97到1.97版本)引導kernel的時候,setup.bin纔會被執行到,也就是僅在該情況下內存探測纔是由kernel引導的領頭羊去完成的。
    
    到了Grub2(即Grub 1.98到現在最新版本)引導linux系統的時候,則由Grub直接探明內存佈局,然後解析vmlinuz文件,並且直接加載vmlinux部分的內容到內存中並跳轉執行head_32函數,而內存佈局則通過參數boot_params傳遞執行。
    
    誰探明內存佈局對內存管理有影響嗎?沒影響的,所以這裏是可以忽略的廢話。既然廢話就多說兩句,爲什麼要分開setup.bin和vmlinux呢?這是因爲setup.bin運行在實模式下面,而vmlinux則運行在保護模式下面。所以也就是說grub2是進入了保護模式後才加載引導的kernel。

1297993-20211114121928779-829326418.png

我們從上面的描述中知道了,內存讀取這個動作發起人,不一定是內核,以現在GRUB作爲操作系統引導程序來說,內存讀取這個動作是由GRUB來發起觸發BISO中斷,進行內存探測的,這其中講到實模式和保護模式,我們後續會講到 。

內存讀取以後

管理-探知的e820表如何處理?是如何被管理的

    前面已經知道了如何探明內存空間得到e820圖,得知了內存的位置、大小和類型。在e820__memory_setup()函數內會將重疊的內存空間根據屬性進行篩選,並將同屬性的相鄰內存空間進行合併處理。整個處理過程如右側所示,雖然實現上會對內存進行分割合併處理,但是實際上內存並不會這麼錯亂重疊的。處理完畢後,會通過e820__print_table()對外打印。通過dmesg可以看到如圖左側所示的信息。

1297993-20211115224925206-612173490.png

那麼是如何分割和合並的呢? 可以看到重疊的部分就會合並,而分散的部分則是合併 ,

使用-內存(指被 Memblock 管理的內存)劃分分配

    內存是連續的嗎?

    物理內存是連續的,但是細心的話,可以發現e820提供的數據中並不連續,中間0xA0000到0xFFFFF的內存並未在其中。這是歷史原因遺留下來的,它並非不存在,而是被BIOS保留下來用作顯卡顯存的映射以及BIOS自留給ROM使用的空間。所以呈現出來有一個空洞位置。


    對此我們可以通過/proc/iomem查看到這些物理內存被如何劃分分配。iomem主要呈現系統中設備的物理佈局,包括未被e820所呈現的,它甚至能夠將kernel在物理內存的加載位置呈現出來。

1297993-20211115231800698-890452300.png

我們這裏就可以知道 dmesg 命令看到的是計算機開始探測到的內存空間 ,而 cat /proc/iomem 則是BIOS虛擬地址在物理內存上的一個映射可以看見貌似只使用了 1MB 的空間 ,關於着 1MB 的相關知識點見 A20

那麼 Memblock 上的內存 又是如何被分配出去的呢 ? Memblock 內存維護着兩個 struct :

  • memory : 指向系統可用物理內存區

  • reserved : 指向系統預留區 (就是給分配出去了)

      memblock管理表由命名爲memblock的全局數據結構變量管理,它主要通過可用內存memory和保留內存reserved兩個成員結構體變量區分管理。
      
      例如可用內存全部都掛入到memblock.memory.regions下,該可用內存同時又以全局變量數組memblock_memory_init_regions而命名,該數組成員主要記錄內存的基址、大小和類型,如圖顯示的是該算法的管理結構關係。類似的被保留的內存則在memblock_reserved_init_regions全局數組結構下管理。
      
      於此階段,我們可以通過memblock_alloc()和memblock_free()對內存進行申請釋放,而分配的方式很簡單,根據需要分配的size到可用的內存空間memblock_memory_init_regions中去查找連續的等大小空間,然後將其分割開來,將分配出去的掛入到memblock_reserved_init_regions管理區中,而剩餘的則放回到memblock_memory_init_regions。尤其是如果我們需要申請永久保留的內存可在此申請,即後續內存管理將不會對此內存進行分配回收管理。
      
      memblock內存管理只是一個過渡形態,不會長期存在,畢竟如此任意分割內存的分配方式長久運行後會導致嚴重的碎片化。因此後面將會建立內存映射,構造內存管理框架。
    

來自參考資料中的描述我們知道此階段的內存分配非常簡單,就是在兩個數組裏任意分配,有可能會造成內存碎片化, 但是此時分配的內存是給什麼的呢?上面分配的內存就是給上圖用的。

具體相關的底層邏輯見 :

1297993-20211115233213443-178650984.png

1297993-20211115233237146-788941172.png

1297993-20211115233300536-4434451.png

查看讀取出來的內存夠空間

Linux dmesg 命令, kernel 會將開機信息存儲在 ring buffer 中。您若是開機時來不及查看信息,可利用 dmesg 來查看。開機信息亦保存在 /var/log 目錄中,名稱爲 dmesg 的文件裏。
命令 :

 dmesg | less

1297993-20211115224016170-839141824.png

查看虛擬地的映射的分佈

cat /proc/iomem

1297993-20211115224303385-779752500.png

虛擬地址映射的內容

其他

中斷

關於“中斷”,參考資料中有篇文章有詳細介紹到。
中斷,英文名爲Interrupt,計算機的世界裏處處都有中斷,任何工作都離不開中斷,可以說整個計算機系統就是由中斷來驅動的。那麼什麼是中斷?簡單來說就是CPU停下當前的工作任務,去處理其他事情,處理完後回來繼續執行剛纔的任務,這一過程便是中斷。

兩張圖可以知道中斷的類型

1297993-20211114120844382-1583466236.jpg

總結

這一節主要是講介紹物理內存空間是如何被讀取出來的,然後讀取出來以後又是如何被管理和分配的,其中涉及到 BIOS 的相關知識點,比如 BIOS 中斷調用,還有包括memblock 這個結構去管理內存的,在 使用-內存(指被 Memblock 管理的內存)劃分分配 章節我們也知道了BIOS 在 memblock 上申請了 1MB 的空間來映射一些內容,包括 BIOS 映射區, DOS 暫存區等 。

最後我們還介紹了兩個查看內存相關的方式 ,一個是查看物理空間被讀取出來後的內存全貌,一個是虛擬地址上的內存分佈, 各個位置放着什麼東西 。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章