pos機(jī)引導(dǎo)者

 新聞資訊2  |   2023-07-13 09:51  |  投稿人:pos機(jī)之家

網(wǎng)上有很多關(guān)于pos機(jī)引導(dǎo)者,引導(dǎo)內(nèi)存分配器的知識,也有很多人為大家解答關(guān)于pos機(jī)引導(dǎo)者的問題,今天pos機(jī)之家(www.www690aa.com)為大家整理了關(guān)于這方面的知識,讓我們一起來看下吧!

本文目錄一覽:

1、pos機(jī)引導(dǎo)者

pos機(jī)引導(dǎo)者

Linux內(nèi)存三大分配器:引導(dǎo)內(nèi)存分配器,伙伴分配器,slab分配器

一、引導(dǎo)內(nèi)存分配器

1.引導(dǎo)內(nèi)存分配器的作用因?yàn)閮?nèi)核里面有很多內(nèi)存結(jié)構(gòu)體,不可能在靜態(tài)編譯階段就靜態(tài)初始化所有的這些內(nèi)存結(jié)構(gòu)體。另外,在系統(tǒng)啟動過程中,系統(tǒng)啟動后的物理內(nèi)存分配器本身也需要初始化,如伙伴分配器,那么伙伴分配器如何獲取內(nèi)存來初始化自己呢 ?為了達(dá)到這個目標(biāo),我們先實(shí)現(xiàn)一個滿足要求的但是可能效率不高的笨家伙,引導(dǎo)內(nèi)存分配器。用它來負(fù)責(zé)系統(tǒng)初始化初期的內(nèi)存管理, 最重要的, 用它來初始化我們內(nèi)存的數(shù)據(jù)結(jié)構(gòu), 直到我們真正的內(nèi)存管理器被初始化完成并能投入使用, 我們將舊的內(nèi)存管理器丟掉。

2.引導(dǎo)內(nèi)存分配器的原理在Linux內(nèi)核中使用struct bootmem_data來描述一個引導(dǎo)內(nèi)存分配,其節(jié)點(diǎn)結(jié)構(gòu)下的一個成員,也就是說每一個節(jié)點(diǎn)都有一個引導(dǎo)內(nèi)存分配。 引導(dǎo)內(nèi)存分配使用struct bootmem_data結(jié)構(gòu)中的node_bootmem_map這個bitmap來呈現(xiàn)memory的狀態(tài),一個bit代表一個物理頁框,也就是用struct page,如果一個bit為1,表示該page已經(jīng)被分配了,如果bit是0,則表示該page未被分配。為了能夠滿足比一個page還小的內(nèi)存塊的分配,引導(dǎo)內(nèi)存分配器會使用last_pos來記住上次分配所使用的PFN以及上次分配所使用的page內(nèi)的偏移:last_offset,下次分配的時候結(jié)合last_pos和last_offset將細(xì)小的內(nèi)存塊分配盡量集中在相同的page中。

3引導(dǎo)內(nèi)存分配器的缺點(diǎn)盡管引導(dǎo)內(nèi)存分配器不會造成嚴(yán)重的內(nèi)存碎片,但是每次分配過程需要線性掃描搜索內(nèi)存來滿足當(dāng)前的分配。因?yàn)槭菣z查bitmap,所以代價比較昂貴,尤其是最先適配(first fit)算法傾向?qū)⑿K內(nèi)存放置在物理內(nèi)存開頭,但是這些內(nèi)存區(qū)域在分配大塊內(nèi)存時,也需要掃描,所以該過程十分浪費(fèi)。所以早期內(nèi)存分配器在系統(tǒng)啟動后就被棄用的原因。

4.bootmem和memblock的比較但是bootmem也有很多問題. 最明顯的就是外碎片的問題, 因此內(nèi)核維護(hù)了memblock內(nèi)存分配器, 同時用memblock實(shí)現(xiàn)了一份bootmem相同的兼容API, 即nobootmem, Memblock以前被定義為Logical Memory Block( 邏輯內(nèi)存塊),但根據(jù)Yinghai Lu的補(bǔ)丁, 它被重命名為memblock. 并最終替代bootmem成為初始化階段的內(nèi)存管理器。 bootmem是通過位圖來管理,位圖存在地地址段, 而memblock是在高地址管理內(nèi)存, 維護(hù)兩個鏈表, 即memory和reserved。 memory鏈表維護(hù)系統(tǒng)的內(nèi)存信息(在初始化階段通過bios獲取的), 對于任何內(nèi)存分配, 先去查找memory鏈表, 然后在reserve鏈表上記錄(新增一個節(jié)點(diǎn),或者合并) bootmem和memblock都是就近查找可用的內(nèi)存, bootmem是從低到高找, memblock是從高往低找。 在boot傳遞給kernel memory bank相關(guān)信息后,kernel這邊會以memblcok的方式保存這些信息,當(dāng)伙伴系統(tǒng)沒有起來之前,在內(nèi)核中也是要有一套機(jī)制來管理memory的申請和釋放。linux內(nèi)核可以通過宏定義選擇nobootmem 或者bootmem 來在伙伴起來之前管理內(nèi)存。這兩種機(jī)制對提供的API是一致的,因此對用戶是透明的

5.bootmem小分析bootmem結(jié)構(gòu)體位于文件include/linux/bootmem.h:

typedef struct bootmem_data {unsigned long node_min_pfn;//節(jié)點(diǎn)內(nèi)存的起始物理頁號unsigned long node_low_pfn;//節(jié)點(diǎn)內(nèi)存的結(jié)束物理頁號void *node_bootmem_map;//位圖指針,每個物理頁對應(yīng)一位,如果物理頁被分配則對應(yīng)位置一。unsigned long last_end_off;//最后一次分配的頁面內(nèi)的偏移量(字節(jié));如果為0,則使用的頁面已滿unsigned long hint_idx;//最后一次分配的物理頁,下次優(yōu)先考慮從這個物理頁分配struct list_head list;//按內(nèi)存地址排序鏈表頭} bootmem_data_t;

bootmem接口函數(shù): 1)bootmem分配內(nèi)存函數(shù):alloc_bootmem 2)bootmem釋放內(nèi)存函數(shù):free_bootmem

#define alloc_bootmem(x) \\__alloc_bootmem(x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)void __init free_bootmem(unsigned long physaddr, unsigned long size){unsigned long start, end;kmemleak_free_part_phys(physaddr, size);//釋放映射的內(nèi)存start = PFN_UP(physaddr);//查找到起始位置的物理頁end = PFN_DOWN(physaddr + size);//查找到結(jié)束為止的物理頁mark_bootmem(start, end, 0, 0);//把釋放的物理頁對應(yīng)的位清零}

6.memblock結(jié)構(gòu)解析memblock結(jié)構(gòu)體位于include/linux/memblock.h文件:

struct memblock {bool bottom_up;//表示內(nèi)存分配方式,真:從低地址向上分配,假:從高地址向下分配phys_addr_t current_limit;//可分配內(nèi)存的最大物理地址struct memblock_type memory;//可用物理內(nèi)存區(qū)域(包括已分配和未分配的)struct memblock_type reserved;//預(yù)留物理內(nèi)存區(qū)域(預(yù)留起來不可用,例子:設(shè)備樹)#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAPstruct memblock_type physmem;//所有的物理內(nèi)存區(qū)域#endif};struct memblock_type {unsigned long cnt;//區(qū)域數(shù)量unsigned long max;//分配區(qū)域的大小phys_addr_t total_size;//所有區(qū)域的大小struct memblock_region *regions;//區(qū)域數(shù)組指向區(qū)域數(shù)組char *name;//內(nèi)存類型符號名};struct memblock_region {phys_addr_t base;//起始物理地址phys_addr_t size;//長度enum memblock_flags flags;//內(nèi)存區(qū)域標(biāo)志屬性#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAPint nid;//節(jié)點(diǎn)編號#endif};//內(nèi)存區(qū)域標(biāo)志屬性定義enum memblock_flags {MEMBLOCK_NONE = 0x0,//表示沒有特殊要求區(qū)域MEMBLOCK_HOTPLUG = 0x1,//表示可以熱插拔的區(qū)域MEMBLOCK_MIRROR = 0x2,//表示鏡像的區(qū)域,將內(nèi)存數(shù)據(jù)做兩份復(fù)制,分配放在主內(nèi)存和鏡像內(nèi)存中MEMBLOCK_NOMAP = 0x4,//表示不添加到內(nèi)核直接映射區(qū)域,即線性映射區(qū)};

memblock體系的結(jié)構(gòu):

7.memblock接口函數(shù)解析1)memblock添加內(nèi)存區(qū)域函數(shù):

int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size){phys_addr_t end = base + size - 1;memblock_dbg("memblock_add: [%pa-%pa] %pF\",&base, &end, (void *)_RET_IP_);//直接調(diào)用memblock_add_range將內(nèi)存區(qū)塊添加到memblock.memory進(jìn)行管理return memblock_add_range(&memblock.memory, base, size, MAX_NUMNODES, 0);}

我們繼續(xù)追memblock_add_range:

int __init_memblock memblock_add_range(struct memblock_type *type,phys_addr_t base, phys_addr_t size,int nid, enum memblock_flags flags){bool insert = false;phys_addr_t obase = base;phys_addr_t end = base + memblock_cap_size(base, &size);int idx, nr_new;struct memblock_region *rgn;if (!size)return 0;if (type->regions[0].size == 0) {WARN_ON(type->cnt != 1 || type->total_size);type->regions[0].base = base;type->regions[0].size = size;type->regions[0].flags = flags;memblock_set_region_node(&type->regions[0], nid);type->total_size = size;return 0;}repeat:/** The following is executed twice. Once with ?lse @insert and* then with %true. The first counts the number of regions needed* to accommodate the new area. The second actually inserts them.*/base = obase;nr_new = 0;//遍歷所有內(nèi)存塊,與新的內(nèi)存塊比較for_each_memblock_type(idx, type, rgn) {phys_addr_t rbase = rgn->base;phys_addr_t rend = rbase + rgn->size;if (rbase >= end)//新加入的內(nèi)存塊的結(jié)束地址已經(jīng)到了則遍歷結(jié)束break;if (rend <= base)//即加入的內(nèi)存塊的起始地址還沒到則遍歷下一塊continue;/** @rgn overlaps. If it separates the lower part of new* area, insert that portion.*///如果新加入的內(nèi)存起始地址已經(jīng)到了,但是還沒到遍歷的內(nèi)存則插入if (rbase > base) {#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAPWARN_ON(nid != memblock_get_region_node(rgn));#endifWARN_ON(flags != rgn->flags);nr_new++;if (insert)//添加內(nèi)存區(qū)域,也就是填充struct memblock_region而已memblock_insert_region(type, idx++, base,rbase - base, nid,flags);}/* area below @rend is dealt with, forget about it */base = min(rend, end);}/* insert the remaining portion */if (base < end) {nr_new++;if (insert)memblock_insert_region(type, idx, base, end - base,nid, flags);}//如果需要加入的內(nèi)存塊個數(shù)為0則返回,不需要第二次遍歷執(zhí)行加入操作if (!nr_new)return 0;/** If this was the first round, resize array and repeat for actual* insertions; otherwise, merge and return.*///第一次會進(jìn)入,判斷內(nèi)存區(qū)域塊是否達(dá)到上限,是則退出,否則回到repeat//因?yàn)閕nsert參數(shù)原因,第一次沒有真正插入,第二次才會真正的插入if (!insert) {while (type->cnt + nr_new > type->max)if (memblock_double_array(type, obase, size) < 0)return -ENOMEM;insert = true;goto repeat;} else {memblock_merge_regions(type);//合并相鄰且沒有縫隙的內(nèi)存區(qū)域return 0;}}

2)memblock刪除內(nèi)存區(qū)域函數(shù):memblock_remove

int __init_memblock memblock_remove(phys_addr_t base, phys_addr_t size){phys_addr_t end = base + size - 1;memblock_dbg("memblock_remove: [%pa-%pa] %pS\",&base, &end, (void *)_RET_IP_);return memblock_remove_range(&memblock.memory, base, size);}

memblock_remove_range:

static int __init_memblock memblock_remove_range(struct memblock_type *type,phys_addr_t base, phys_addr_t size){int start_rgn, end_rgn;int i, ret;//要刪除的內(nèi)存區(qū)域內(nèi)存區(qū)內(nèi)的內(nèi)存塊存在重疊部分,把這部分需要獨(dú)立出來ret = memblock_isolate_range(type, base, size, &start_rgn, &end_rgn);if (ret)return ret;//根據(jù)要刪除內(nèi)存區(qū)的索引號,刪除內(nèi)存區(qū)塊for (i = end_rgn - 1; i >= start_rgn; i--)memblock_remove_region(type, i);return 0;}

3)memblock分配內(nèi)存函數(shù):memblock_alloc

phys_addr_t __init memblock_alloc(phys_addr_t size, phys_addr_t align){return memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE);}phys_addr_t __init memblock_alloc_base(phys_addr_t size, phys_addr_t align, phys_addr_t max_addr){phys_addr_t alloc;alloc = __memblock_alloc_base(size, align, max_addr);if (alloc == 0)panic("ERROR: Failed to allocate %pa bytes below %pa.\",&size, &max_addr);return alloc;}phys_addr_t __init __memblock_alloc_base(phys_addr_t size, phys_addr_t align, phys_addr_t max_addr){return memblock_alloc_base_nid(size, align, max_addr, NUMA_NO_NODE,MEMBLOCK_NONE);}phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size,phys_addr_t align, phys_addr_t max_addr,int nid, enum memblock_flags flags){return memblock_alloc_range_nid(size, align, 0, max_addr, nid, flags);}static phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size,phys_addr_t align, phys_addr_t start,phys_addr_t end, int nid,enum memblock_flags flags){phys_addr_t found;if (!align)align = SMP_CACHE_BYTES;//在給定范圍和節(jié)點(diǎn)內(nèi)找一塊空區(qū)域found = memblock_find_in_range_node(size, align, start, end, nid,flags);//memblock_reserve是把找到的空區(qū)域添加到memblock.reserved中,表示已經(jīng)用了if (found && !memblock_reserve(found, size)) {/** The min_count is set to 0 so that memblock allocations are* never reported as leaks.*///一個內(nèi)存塊分配物理內(nèi)存的通知kmemleak_alloc_phys(found, size, 0, 0);return found;}return 0;}

4)memblock釋放內(nèi)存函數(shù):memblock_free

int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size){phys_addr_t end = base + size - 1;memblock_dbg(" memblock_free: [%pa-%pa] %pF\",&base, &end, (void *)_RET_IP_);//通知釋放部分內(nèi)存塊kmemleak_free_part_phys(base, size);return memblock_remove_range(&memblock.reserved, base, size);}static int __init_memblock memblock_remove_range(struct memblock_type *type,phys_addr_t base, phys_addr_t size){int start_rgn, end_rgn;int i, ret;//要刪除的內(nèi)存區(qū)域內(nèi)存區(qū)內(nèi)的內(nèi)存塊存在重疊部分,把這部分需要獨(dú)立出來ret = memblock_isolate_range(type, base, size, &start_rgn, &end_rgn);if (ret)return ret;//根據(jù)要刪除內(nèi)存區(qū)的索引號,刪除內(nèi)存區(qū)塊for (i = end_rgn - 1; i >= start_rgn; i--)memblock_remove_region(type, i);return 0;}

7.memblock啟動流程 1)解析設(shè)備樹中的/memory,把所有物理內(nèi)存添加到memblock 2)在memblock_init中初始化memblock linux啟動從init/main.c文件的start_kernel函數(shù)開始,然后從文件setup_arch(arch/arm64/kernel/setup.c文件中)函數(shù)檢測處理器類型,初始化處理器和內(nèi)存,其中的arm64_memblock_init(arch/arm64/mm/init.c文件中)函數(shù)就是arm64架構(gòu)的memblock初始化流程。

void __init arm64_memblock_init(void){const s64 linear_region_size = -(s64)PAGE_OFFSET;/* Handle linux,usable-memory-range property *///解析設(shè)備樹文件的內(nèi)存節(jié)點(diǎn)fdt_enforce_memory_region;/* Remove memory above our supported physical address size *///刪除超出我們支持的物理地址大小的內(nèi)存memblock_remove(1ULL << PHYS_MASK_SHIFT, ULLONG_MAX);/** Ensure that the linear region takes up exactly half of the kernel* virtual address space. This way, we can distinguish a linear address* from a kernel/module/vmalloc address by testing a single bit.*/BUILD_BUG_ON(linear_region_size != BIT(VA_BITS - 1));/** Select a suitable value for the base of physical memory.*///全局變量memstart_addr記錄了內(nèi)存的起始物理地址memstart_addr = round_down(memblock_start_of_DRAM,ARM64_MEMSTART_ALIGN);/** Remove the memory that we will not be able to cover with the* linear mapping. Take care not to clip the kernel which may be* high in memory.*///把線性映射區(qū)無法覆蓋的物理內(nèi)存范圍從memblock中刪除memblock_remove(max_t(u64, memstart_addr + linear_region_size,__pa_symbol(_end)), ULLONG_MAX);if (memstart_addr + linear_region_size < memblock_end_of_DRAM) {/* ensure that memstart_addr remains sufficiently aligned */memstart_addr = round_up(memblock_end_of_DRAM - linear_region_size,ARM64_MEMSTART_ALIGN);memblock_remove(0, memstart_addr);}/** Apply the memory limit if it was set. Since the kernel may be loaded* high up in memory, add back the kernel region that must be accessible* via the linear mapping.*///如果設(shè)置了內(nèi)存限制,要根據(jù)限制使用內(nèi)存if (memory_limit != PHYS_ADDR_MAX) {memblock_mem_limit_remove_map(memory_limit);//把超出限制的內(nèi)存移除memblock_add(__pa_symbol(_text), (u64)(_end - _text));//添加可以使用的內(nèi)存}if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && initrd_start) {/** Add back the memory we just removed if it results in the* initrd to become inaccessible via the linear mapping.* Otherwise, this is a no-op*/u64 base = initrd_start & PAGE_MASK;u64 size = PAGE_ALIGN(initrd_end) - base;/** We can only add back the initrd memory if we don\'t end up* with more memory than we can address via the linear mapping.* It is up to the bootloader to position the kernel and the* initrd reasonably close to each other (i.e., within 32 GB of* each other) so that all granule/#levels combinations can* always access both.*/if (WARN(base < memblock_start_of_DRAM ||base + size > memblock_start_of_DRAM +linear_region_size,"initrd not fully accessible via the linear mapping -- please check your bootloader ...\")) {initrd_start = 0;} else {memblock_remove(base, size); /* clear MEMBLOCK_ flags */memblock_add(base, size);memblock_reserve(base, size);}}if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {extern u16 memstart_offset_seed;u64 range = linear_region_size -(memblock_end_of_DRAM - memblock_start_of_DRAM);/** If the size of the linear region exceeds, by a sufficient* margin, the size of the region that the available physical* memory spans, randomize the linear region as well.*/if (memstart_offset_seed > 0 && range >= ARM64_MEMSTART_ALIGN) {range /= ARM64_MEMSTART_ALIGN;memstart_addr -= ARM64_MEMSTART_ALIGN *((range * memstart_offset_seed) >> 16);}}/** Register the kernel text, kernel data, initrd, and initial* pagetables with memblock.*///把內(nèi)核鏡像占用的內(nèi)存添加到memblock的預(yù)留區(qū)中,表示預(yù)留了不再分配出去memblock_reserve(__pa_symbol(_text), _end - _text);#ifdef CONFIG_BLK_DEV_INITRDif (initrd_start) {memblock_reserve(initrd_start, initrd_end - initrd_start);/* the generic initrd code expects virtual addresses */initrd_start = __phys_to_virt(initrd_start);initrd_end = __phys_to_virt(initrd_end);}#endif//掃描設(shè)備樹中的保留內(nèi)存區(qū)域并添加到memblock的預(yù)留區(qū)域中early_init_fdt_scan_reserved_mem;/* 4GB maximum for 32-bit only capable devices */if (IS_ENABLED(CONFIG_ZONE_DMA32))arm64_dma_phys_limit = max_zone_dma_phys;elsearm64_dma_phys_limit = PHYS_MASK + 1;reserve_crashkernel;reserve_elfcorehdr;high_memory = __va(memblock_end_of_DRAM - 1) + 1;dma_contiguous_reserve(arm64_dma_phys_limit);memblock_allow_resize;}

最后,引導(dǎo)內(nèi)存分配器退休,會將物理內(nèi)存填充到伙伴分配器中,移交給伙伴分配器進(jìn)行管理。

end

人人極客社區(qū)

關(guān)注,回復(fù)【peter】海量Linux資料贈送

文章推薦

?【專輯】

?【專輯】

?【專輯】

?【專輯】

?【專輯】

?【專輯】

?【專輯】

?【專輯】

?

?【專輯】

以上就是關(guān)于pos機(jī)引導(dǎo)者,引導(dǎo)內(nèi)存分配器的知識,后面我們會繼續(xù)為大家整理關(guān)于pos機(jī)引導(dǎo)者的知識,希望能夠幫助到大家!

轉(zhuǎn)發(fā)請帶上網(wǎng)址:http://www.www690aa.com/newsone/84279.html

你可能會喜歡:

版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn),該文觀點(diǎn)僅代表作者本人。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請發(fā)送郵件至 babsan@163.com 舉報,一經(jīng)查實(shí),本站將立刻刪除。