linux bootmem memblock的演進

有人可能認爲在系統啓動的時候內存的分配應該非常容易:基本上所有的內存都是可用的,不需要考慮併發性。儘管如此,boot階段的內存管理仍然不是一個簡單的任務。物理內存不一定是連續的,系統之間他們的分佈更是有很大的不同,如何檢測內存的佈局就不是一個簡單的任務。在NUMA的機器上這件事情更加複雜,在內存分配的時候還需要考慮在當前節點上分配,構建內存的拓撲圖。爲了應對這些問題,在啓動過程中的早期階段需要一些複雜的機制來進行內存管理工作。

有人會問,爲什麼不一開始就使用buddy這些系統來管理呢?因爲linux的頁分配管理器本身就很複雜,也需要分配內存來初始化自身,所以在早期階段page分配器是不可用的。另外在NUMA上頁分配器的數據結構應該帶有NUMA的特徵,即在node上分配該cpu的頁面申請請求。所以需要額外的內存管理器來管理內存,直至內存管理系統狀態變爲可用的。

在linux早期開發階段,linux並沒有boot內存管理;在1.0內核中,內存初始化並沒有像今天一樣健壯和靈活。早期的子系統初始化僅僅是調用start_kernel,它通過全局的memory_start變量來訪問單個bank內存的起始地址。如果需要申請內存,只需要像sbrk一樣增長memory_start來表示內存被佔用。在2.0版本中,linux已經被移植到多於5個平臺上,不過boot階段的內存管理仍然和1.0版本上一樣簡單,唯一不一樣的是增加了平臺探測自身可用內存的代碼,而且那時候的硬件更加簡單,可以很輕鬆的探測到內存信息。

一直到2.3.23pre3版本上,boot早期的內存分配仍然使用全局變量來表示可用內存的起始和結束地址。很幸運的是page和slab分配器在boot不久就可用了,所以嚴重依賴內存的功能,像buffers_init() and page_cache_init()就直接可以使用core的內存管理器。硬件演進的越來越複雜,平臺相關代碼處理內存變得也越來越複雜。

在2.3.23pre3的patch中第一次包含了bootmem分配器的視線,它使用一個bitmap來代表頁的狀態,0代表可用,1代表頁缺失或者已經被使用。所有操作全局變量memory_start的函數還有i386的初始化代碼開始使用bootmem,一直到2.3.48版本這種轉換才完成,於此同時其他平臺仍然使用老的方式。當linux被移植到Itanium(ia64)平臺上時,它就完全使用bootmem。

隨着時間的推移,內存佈局探測也變的更加複雜,從剛開始只需要向BIOS詢問內存塊的大小到後來需要處理複雜的表,內存片,內存塊還有內存簇。Power64時一個使用LMB(Logical Memory Block allocator)的平臺,在LMB中,使用兩個區間數組來表示內存信息,第一個數組描述了系統中可用的物理上連續的內存,第二個數組描述了已經被申請走的區域。LMB分配器之後被SPARC平臺使用,之後其他的平臺也接受了這個管理,逐步演變成了現在的memblock.

memblock分配器提供了兩個最基本的接口,其他複雜的分配api都是基於這兩個接口的:memblock_addmemblock_reserve,前者用來添加可用物理內存,後者用來表示被使用的內存,他們最終都是通過memblock_add_range來向兩個數組中添加區間。

bootmem最主要的問題時它的bitmap初始化,爲了創建bitmap,必須要知道物理內存的配置。第一個時bitmap究竟應該多大,哪塊內存有這樣足夠的連續物理內存來存儲它?而且隨着內存增大,bitmap佔用的空間也增大了,在一個具有32GB的RAM中,bitmap需要佔據1MB的空間。而memblock它是基於一個非常大的靜態數組,它可以隨時使用的,最起碼第一個內存的註冊和分配是沒有問題的。如果需要添加或者預留內存的區間數量超過了靜態數組的大小,這個數組會double一下,而這種情況發生的時候我們相信會有足夠的內存來存放擴張之後的數組。

memblock的設計時,它就假設在core的頁分配器可用之前,不會有太多場景需要頻繁申請和釋放內存的,所以它並不沒有做的非常複雜和職能,在將所有可用內存釋放給buddy管理器之後就完全沒有作用了。

爲了過度bootmem到memblock,一個兼容層nobootmem被設計出來。nobootmem提供了bootmem一樣的api,但是底層沒有使用bitmap來管理頁的狀態而是使用了memblock的管理數據。在4.17版本,24個平臺中只有5個仍然使用bootmem,14個平臺使用nobootmem,剩餘的5個平臺同時支持memblock和bootmem。

當前正在進行的工作是在所有平臺上都過渡到memblock和nobootmem。幾個平臺上使用設備樹來進行內存管理的因爲最近的變動也轉而使用memblock,alpha,c6x,m68k和nios2平臺上已經發patch在review了,其中的一些已經被arch maintaine合併了,另外一些還在review中。

希望在4.20合併窗口中所有平臺都不再使用bootmem,之後就很可能會開始清理早期的內存管理代碼,包括移除bootmem分配器和相關的內核配置。之後可能會整合更早期的啓動代碼,從平臺完全相關到通用框架管理(譯者:你們要不要搞這麼複雜,輪子造的我都看不過來了),在內存管理系統中從來不存在完美這一說。

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