接着上一篇《Linux內核源碼分析之setup_arch (一)》繼續分析,本文首先分析arm_memblock_init函數,而後分析內核啓動階段的是如何進行內存管理的。node
該函數的功能比較簡單,主要就是把meminfo中記錄的內存條信息添加到memblock.memory中,而後把內核鏡像所在內存區域添加到memblock.reserved中,arm_mm_memblock_reserve把頁表所在內存區域添加到memblock.reserved中;若是使用了設備樹,則使用arm_dt_memblock_reserve來保留所佔用的內存,最後則是調用CPU相關的mdesc->reserve,其對應的調用爲cpu_mem_reserve,該函數定義在cpu.c中。linux
/* arch/arm/mm/init.c */ void __init arm_memblock_init(...) { for (i = 0; i < mi->nr_banks; i++) memblock_add(mi->bank[i].start, mi->bank[i].size); memblock_reserve(__pa(_stext), _end - _stext); arm_mm_memblock_reserve(); arm_dt_memblock_reserve(); if (mdesc->reserve) mdesc->reserve(); arm_memblock_steal_permitted = false; memblock_allow_resize(); memblock_dump_all(); } /* include/kernel/memblock.h */ struct memblock { phys_addr_t current_limit; struct memblock_type memory; struct memblock_type reserved; };
接下來就該執行paging_init函數了,在分析paging_init以前先來點內核啓動階段的內存管理相關的內容。從arm_memblock_init開始引入memblock數據結構,其做用是實現內核啓動初期的內存管理功能,嚴格來講,其生命週期到paging_init::bootmem_init爲止,memblock_alloc調用流程以下。數據結構
實際查找空閒內存的函數爲memblock_find_in_range_node,而該函數中真正實現空閒內存查找的是for_each_free_mem_range_reverse這個宏定義。函數
/* mm/memblock.c */ phys_addr_t memblock_find_in_range_node(...) { ... for_each_free_mem_range_reverse(i, nid, &this_start, &this_end, NULL) { ... if (cand >= this_start) return cand; } return 0; }
該宏定義以下,然而其中又嵌套了一個函數Orz...源碼分析
/* include/linux/memblock.h */ #define for_each_free_mem_range_reverse(i, nid, p_start, p_end, p_nid) \ for (i = (u64)ULLONG_MAX, \ __next_free_mem_range_rev(&i, nid, p_start, p_end, p_nid); \ i != (u64)ULLONG_MAX; \ __next_free_mem_range_rev(&i, nid, p_start, p_end, p_nid))
首先須要說明的是,memblock.reserved標識的區域表示的是已被佔用的內存區域,memblock.memory中記錄的是內存條信息。如今回到__next_free_mem_range_rev函數,代碼段(1)(2)的目的是找出內存條上兩個reserved區域之間的內存區域,即空閒區域。找到以後再通過代碼段(3)對空閒區域的起始地址和結束地址進行修正,由於代碼段(1)(2)只能保證空閒區與當前內存條存在交集,並不能保證該空閒區域徹底處於當前內存條之中,主要緣由在於沒法保證這兩個reserved區域都在當前內存條上。this
/* mm/memblock.c */ void __init_memblock __next_free_mem_range_rev(...) { struct memblock_type *mem = &memblock.memory; struct memblock_type *rsv = &memblock.reserved; ... /* (1) */ for ( ; mi >= 0; mi--) { struct memblock_region *m = &mem->regions[mi]; phys_addr_t m_start = m->base; phys_addr_t m_end = m->base + m->size; ... /* (2) */ for ( ; ri >= 0; ri--) { struct memblock_region *r = &rsv->regions[ri]; phys_addr_t r_start = ri ? r[-1].base + r[-1].size : 0; phys_addr_t r_end = ri < rsv->cnt ? r->base : ULLONG_MAX; ... /* (3) */ if (m_end > r_start) { if (out_start) *out_start = max(m_start, r_start); if (out_end) *out_end = min(m_end, r_end); if (out_nid) *out_nid = memblock_get_region_node(m); ... return; } } } *idx = ULLONG_MAX; }
至此,空閒區域的查找基本就結束了,回到memblock_find_in_range_node函數中,再檢查一下該區域的起始地址和結束地址是否合法等等,最終就申請到了所請求大小的內存區域,最後只須要將這塊內存區域標記爲reserved狀態就結束了內存分配的整個過程了。code
/* mm/memblock.c */ int memblock_reserve(phys_addr_t base, phys_addr_t size) { struct memblock_type *_rgn = &memblock.reserved; return memblock_add_region(_rgn, base, size, MAX_NUMNODES); }