Linux Kernel啟動過程中的記憶體管理

2021-12-29 20:05:52 字數 3994 閱讀 1798

linux kernel啟動過程中的記憶體管理

好的作業系統必然要有好的記憶體管理系統來支援。好的記憶體管理系統就像乙個藝術品,因為在其中我們可以看到空間優化和時間優化的完美平衡(既要省記憶體又要分配和釋放足夠快)。linux為我們提供了這樣乙個範例,關於它的記憶體管理在很多講kernel的書都可以找到。但在這一切還沒有建立起來時,系統又是怎麼工作的呢?

在系統啟動時記憶體分配大致經歷了這樣幾個階段(基於kernel 2.6.29):

1. 靜態分配(如果這也算一種的話。。。)

2. e820表

3. bootmem allocator

4. zone allocator(buddy system)

5. slab allocator

6. 虛擬空間分配,如用vmalloc, mmap這些函式分配

當然以上的幾個階段的時間界限並不總是很明顯,有些時候是並存的。以下是初始化**中幾個關鍵點:

start_kernel()  

setup_arch()  

setup_memory_map() //從boot_params.e820_map讀入e820表資訊,以後就可以用find_e820_area()分配了。  

init_memory_mapping()  

kernel_physical_mapping_init() //在虛擬空間對映kernel頁表的low mem部分,建立頁表過程中需要分配記憶體就是通過find_e820_area()。  

initmem_init()    

setup_bootmem_allocator() //初始化bootmem allocator,bootmem allocator可用。  

early_res_to_bootmem() //把之前靜態分配或者從通過find_e820_area()分配的區域置成保留。  

paging_init() //完成kernel頁表high mem中persistent kernel mapping和temporary kernel mapping部分的初始化,之後可以通過kmap()或kmap_atomic()把物理頁對映到high mem區域。  

zone_sizes_init() //初始化zone allocator,但只是初始化,還沒法用它分配,因為所有的freelist還是被置成空的。  

vmalloc_init() //初始化分配noncontiguous memory area所需要的結構。vmalloc能分配high mem中從vmalloc_start到vmalloc_end的虛擬空間。加上前面paging_init()中提到的兩種,針對kernel的high mem的三種對映方式就全了。該函式中通過bootmem allocator分配自身需要的記憶體。  

mem_init() //完成zone allocator,也就是buddy system的初始化,之後alloc_page()就可以用了。這裡將bootmem allocator中的未分配空間轉到zone allocator中,然後禁用了bootmem allocator。  

kmem_cache_init() //初始化slab allocator。它是zone allocator上的一層加強,彌補了zone allocator的一些固有不足,如只能以2的n次冪分配物理頁。kmalloc()會從slab allocator上分配,而slab allocator中cache不夠又會從zone allocator分配。  

系統啟動剛開始的一些資料是靜態分配的,如kernel本身的**段和資料段,因為這時還沒有任何分配器存在。這些都被loader存放在固定的實體地址,並被臨時頁表對映到固定的虛擬位址。

e280表和find_e820_area()可以稱得上最早的allocator,儘管它很簡單。系統啟動早期,detect_memory()函式中,系統通過15h中斷從bios中讀取物理記憶體資訊,將之放到boot_params(也就是zeropage中)。之後set_memory_map()函式將這些資訊再讀入e820結構體裡,之後find_e820_area()就可以從裡面分配記憶體了。分配方式採用簡單的線性查詢,並把分配出去的空間通過reserve_early()記錄到early_res這個結構中,這些資訊將會在bootmem allocator的初始化時用來置位那些已分配的物理記憶體區域。舉例來說,當系統要建立kernel頁表時,需要申請頁表本身所佔的記憶體,於是呼叫one_page_table_init(),它發現bootmem allocator尚不可用,於是呼叫alloc_low_page(),這個函式就會到[table_start, start_end]這個區域裡去拿記憶體,而這塊記憶體是在之前find_early_table_space()中通過find_e820_area()申請出來的。當kernel頁表建立完後,reserve_early()被呼叫,它將[table_start, table_end]這塊區域以"pgtable"為label記錄下來。

然後是bootmem allocator,它和e820直接分配一樣,也是乙個中間過渡產物。bootmem allocator,顧名思義就是在系統啟動時候用的臨時記憶體分配器。它在zone allocator建立好之後就被禁用,而在其中仍然空閒的區域會被**到zone allocator中。bootmem allocator是一種基於bitmap的分配器,因此速度也很快。從bootmem allocator分配使用函式alloc_bootmem()。

再就是zone allocator。我們知道典型的系統上有三個zone:zone_dma,zone_normal,zone_highmem。系統為每個zone都建立了我們熟悉的buddy system。簡單地說,buddy system把物理記憶體區域按2的n次方(n稱為order)掛在zone->free_area上。分配的時候拆分它們直到滿足分配申請要求,釋放的時候再進行合併。從zone allocator分配和釋放分別用函式alloc_page()和free_page()。

buddy system非常高效,但帶來了內部碎片問題,因為它只能分配出2的0次方到2的max_order次方的頁,而且它也不利於硬體cache的利用。而核心中經常會頻繁申請固定大小的記憶體如process descriptor, open file object等。如果每次都從buddy system中申請,既費時間又費空間。出於空間和時間的效率考慮,於是有了slab allocator。slab allocator相當於將記憶體資源按每種固定大小進行快取,放在cache中。系統要的時候直接從這個cache裡拿,而釋放時則不是真的釋放,而是放回到cache中。slab allocator維護很多cache,每個cache中包含同一型別的boject。cache又劃分為slab,slab通常包含幾個連線的物理頁,其中存放在被分配的或者尚空閒的object。對於slab allocator中的記憶體資源,呼叫kmalloc()或者直接呼叫kmem_cache_alloc()進行分配,呼叫kfree()或者直接呼叫kmem_cache_free()進行釋放。當kmem_cache_alloc()被呼叫且cache中沒有object時,會呼叫cache_grow()來增加cache中的slab,這也是slab的建立過程。cache_grow()繼而呼叫kmem_getpages()為object分配物理記憶體。而kmem_getpages最終會到buddy system中去分配(kmem_getpages() => alloc_pages_node() => __alloc_pages())。因此我們說slab allocator不是buddy system的替代,還是加強。

之後,系統的記憶體管理系統就初步建立好了。系統中的記憶體資源有兩種-虛擬空間和物理空間。在kernel態,用vmalloc()申請虛擬空間,同時它也呼叫了alloc_page()申請物理空間,再對映到虛擬空間中。而kmalloc可以直接從slab allocator中申請物理空間,而slab allocator中如果記憶體不夠了再到buddy system中去分配。這樣申請來的物理空間在虛擬位址空間中還沒有顯式對映,當然了,如果是low mem部分則已經在系統初始化時被對映到page_offset處了。而user態中如果app呼叫malloc這樣的函式,malloc會呼叫mmap,而mmap會申請呼叫程序的虛擬空間,這裡虛擬空間還沒有對應的物理頁。只有真地訪問時發生page fault了,在pagefault handler裡才會從buddy system中去分配物理頁。

mysql啟動過程長 mysql啟動過程

mysql啟動過程經過以下順序 1.mysql讀取配置檔案的順序 讀取順序 etc my.cnf etc mysql my.cnf usr etc my.cnf my.cnf 2.mysql啟動方式 mysql啟動方式有三種 mysqld mysqld safe mysqld multi 主要用於多...

Linux啟動過程

1 開啟 pc電源 pc加電後,乙個特殊的硬體電路會觸發 cpu 的 reset 腳的邏輯值,然後一些 cpu 暫存器,如 cs eip 等被給定乙個值,接著 cpu 跳轉到乙個固定位址開始執行 bios 2 bios 自檢,按設定的裝置啟動 bios 對硬體進行一系列徹底的檢查,如 cpu 型別 ...

Flex 啟動過程

2008 11 16 11 58 然後才是 通常我們所說的flex應用程式,本質上來說是基於flex框架 採用actionscript 3.0編寫的flash應用程式,從這一點來說,它和普通的flash應用程式沒有任何差別。相對來說,傳統的使用flash ide 如flash cs3 建立flash...