首页 > 代码库 > 内存管理初始化源码3:bootmem
内存管理初始化源码3:bootmem
start_kernel ——> setup_arch ——> arch_mem_init ——> bootmem_init ——> init_bootmem_node:
此时,不得不说的就是 bootmem 。
1. 什么是bootmem:
我们都知道,所有的物理内存是交给内核管理的,或者说是交给内存管理子系统管理的。那么,从内核启动到内核管理子系统启动之间,是否需要内存呢?答案是肯定的,该时间段内是需要物理内存的。
那么bootmem就是负责该时间段的物理内存的分配。
2. 特性:
简单
该分配器的需求集中于简单性方面,而不是性能和通用性。因此内核开发者决定实现一个最先适配(first-fit)分配器用于在启动阶段管理内存。
3. 基本原理
用一个位图来管理页,位图比特位的数目与系统中物理内存页的数据相同。比特位为1,表示已用页;比特位为0,表示空闲页。
在需要分配内存时,分配器逐位扫描位图,直至找到一个能够提供足够连续页的位置,即所谓的最先最佳(first-best)或最先适配的位置。
4. 初始化
/** * init_bootmem_node - register a node as boot memory * @pgdat: node to register 【属于某个内存结点的bootmem】 * @freepfn: pfn where the bitmap for this node is to be placed 【该内存结点的物理内存页的位图所存内存的pfn】 * @startpfn: first pfn on the node 【该内存的结点的 first pfn】 * @endpfn: first pfn after the node 【该内存将诶点的 end pfn】 * * Returns the number of bytes needed to hold the bitmap for this node. 【返回管理该结点所有内存页的位图所需的总字节数】 */unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, unsigned long startpfn, unsigned long endpfn){ return init_bootmem_core(pgdat->bdata, freepfn, startpfn, endpfn);}
/**
* 我们传递的参数是: bootmap_size = init_bootmem_node(NODE_DATA(0), mapstart, min_low_pfn, max_low_pfn);
* mapstart = 2358, min_low_pfn = 0, max_low_pfn = 131072
* 注意:1. mapstart之前的页是存储了initrd相关数据,上文已经解释过
* 2. max_low_pfn 为131072是由于此时是将 0 ~ 512M 都认为是低端内存,将 0 ~ 512M 之间的所有物理页都建立了bitmap,其实,我们的低端物理内存页只有 0 ~ 57344
*/
/* * Called once to set up the allocator itself. */static unsigned long __init init_bootmem_core(bootmem_data_t *bdata, unsigned long mapstart, unsigned long start, unsigned long end){ unsigned long mapsize; mminit_validate_memmodel_limits(&start, &end); // start 和 end 的合法性检测 bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart)); // mapstart是存储位图的pfn,转换为相应的虚拟地址 bdata->node_min_pfn = start; // 记录 node_min_pfn bdata->node_low_pfn = end; // 记录 node_low_pfn link_bootmem(bdata); // bdata和什么做关联? /* * Initially all pages are reserved - setup_arch() has to * register free RAM areas explicitly.
*/
// 初始化所有保留的页——setup_arch()必须精确的注册所有的RAM区域
mapsize = bootmap_bytes(end - start); // 计算 bitmap 所需的 bytes memset(bdata->node_bootmem_map, 0xff, mapsize); // 将 bitmap 区域设置 0xff bdebug("nid=%td start=%lx map=%lx end=%lx mapsize=%lx\n", bdata - bootmem_node_data, start, mapstart, end, mapsize); return mapsize;}
...
static struct list_head bdata_list __initdata = http://www.mamicode.com/LIST_HEAD_INIT(bdata_list);
...
/* * link bdata in order 【将bdata按顺序连接到临时链表 bdata_list中】 */static void __init link_bootmem(bootmem_data_t *bdata){ struct list_head *iter; list_for_each(iter, &bdata_list) { bootmem_data_t *ent; ent = list_entry(iter, bootmem_data_t, list); if (bdata->node_min_pfn < ent->node_min_pfn) break; } list_add_tail(&bdata->list, iter);
/* 等价于 */ for (iter = (&bdata_list)->next; iter != (&bdata_list); iter = iter->next) {
bootmem_data_t *ent;
ent = container_of(iter, bootmem_data_t, list);
if (bdata->node_min_pfn < ent->node_min_pfn)
break;
}
list_add_tail(&bdata->list, iter); // 将该结点的 bdata 连接到临时链表 bdata_list中, bdata_list的定义在 bootmem.c中
}
static unsigned long __init bootmap_bytes(unsigned long pages){ unsigned long bytes = (pages + 7) / 8; return ALIGN(bytes, sizeof(long));}
5. 将低端内存交给 bootmem allocator 管理
/** * free_bootmem - mark a page range as usable * @addr: starting address of the range * @size: size of the range in bytes * * Partial pages will be considered reserved and left as they are. * * The range must be contiguous but may span node boundaries. */
/**
* 功能:标志一个 page 为可用状态
* @addr : 标记范围的开始地址
* @size : 标记范围的大小(bytes)
*/
void __init free_bootmem(unsigned long addr, unsigned long size){ unsigned long start, end; kmemleak_free_part(__va(addr), size); // 内核内存泄露检测 start = PFN_UP(addr); end = PFN_DOWN(addr + size); mark_bootmem(start, end, 0, 0);}
/**
* free_bootmem(PFN(start), size << PAGE_SHIFT); // start = 2358, size = end - start = 53744 - 2358
*/
内存管理初始化源码3:bootmem
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。