- 论坛徽章:
- 0
|
linux启动内存分配器 3。。。。。。。。。。 - view plaincopy to clipboardprint?/**
- * reserve_bootmem - mark a page range as usable
- * @addr: starting address of the range
- * @size: size of the range in bytes
- * @flags: reservation flags (see linux/bootmem.h)
- *
- * Partial pages will be reserved.
- *
- * The range must be contiguous but may span node boundaries.
- */
- int __init reserve_bootmem(unsigned long addr, unsigned long size,
- int flags)
- {
- unsigned long start, end;
-
- start = PFN_DOWN(addr);/*下界*/
- end = PFN_UP(addr + size);/*上界*/
-
- return mark_bootmem(start, end, 1, flags);
- }
- /**
- * reserve_bootmem - mark a page range as usable
- * @addr: starting address of the range
- * @size: size of the range in bytes
- * @flags: reservation flags (see linux/bootmem.h)
- *
- * Partial pages will be reserved.
- *
- * The range must be contiguous but may span node boundaries.
- */
- int __init reserve_bootmem(unsigned long addr, unsigned long size,
- int flags)
- {
- unsigned long start, end;
- start = PFN_DOWN(addr);/*下界*/
- end = PFN_UP(addr + size);/*上界*/
- return mark_bootmem(start, end, 1, flags);
- }view plaincopy to clipboardprint?/*保留指定内存区间*/
- static int __init mark_bootmem(unsigned long start, unsigned long end,
- int reserve, int flags)
- {
- unsigned long pos;
- bootmem_data_t *bdata;
-
- pos = start;
- /*通过bdata_list链表找到在指定区间的bdata*/
- list_for_each_entry(bdata, &bdata_list, list) {
- int err;
- unsigned long max;
-
- if (pos < bdata->node_min_pfn ||
- pos >= bdata->node_low_pfn) {
- BUG_ON(pos != start);
- continue;
- }
-
- max = min(bdata->node_low_pfn, end);
- /*设置为保留*/
- err = mark_bootmem_node(bdata, pos, max, reserve, flags);
- if (reserve && err) {/*如果出错,递归调用*/
- mark_bootmem(start, pos, 0, 0);
- return err;
- }
-
- if (max == end)
- return 0;
- pos = bdata->node_low_pfn;
- }
- BUG();
- }
- /*保留指定内存区间*/
- static int __init mark_bootmem(unsigned long start, unsigned long end,
- int reserve, int flags)
- {
- unsigned long pos;
- bootmem_data_t *bdata;
- pos = start;
- /*通过bdata_list链表找到在指定区间的bdata*/
- list_for_each_entry(bdata, &bdata_list, list) {
- int err;
- unsigned long max;
- if (pos < bdata->node_min_pfn ||
- pos >= bdata->node_low_pfn) {
- BUG_ON(pos != start);
- continue;
- }
- max = min(bdata->node_low_pfn, end);
- /*设置为保留*/
- err = mark_bootmem_node(bdata, pos, max, reserve, flags);
- if (reserve && err) {/*如果出错,递归调用*/
- mark_bootmem(start, pos, 0, 0);
- return err;
- }
- if (max == end)
- return 0;
- pos = bdata->node_low_pfn;
- }
- BUG();
- }
复制代码 三、内存的分配和释放
介绍了上面的初始化流程,对于分配和释放就简单了,分配就是将分配器映射位图中对应的位置1,释放过程相反。
[code]view plaincopy to clipboardprint?/*分配size大小的空间*/
static void * __init alloc_bootmem_core(struct bootmem_data *bdata,
unsigned long size, unsigned long align,
unsigned long goal, unsigned long limit)
{
unsigned long fallback = 0;
unsigned long min, max, start, sidx, midx, step;
bdebug("nid=%td size=%lx [%lu pages] align=%lx goal=%lx limit=%lx\n",
bdata - bootmem_node_data, size, PAGE_ALIGN(size) >> PAGE_SHIFT,
align, goal, limit);
BUG_ON(!size);
BUG_ON(align & (align - 1));
BUG_ON(limit && goal + size > limit);
/*如果没有映射位图返回空,分配失败*/
if (!bdata->node_bootmem_map)
return NULL;
min = bdata->node_min_pfn;
max = bdata->node_low_pfn;
goal >>= PAGE_SHIFT;
limit >>= PAGE_SHIFT;
if (limit && max > limit)
max = limit;
if (max <= min)
return NULL;
/*step为需要对齐于页面数*/
step = max(align >> PAGE_SHIFT, 1UL);
/*计算起始页面*/
if (goal && min < goal && goal < max)
start = ALIGN(goal, step);
else
start = ALIGN(min, step);
/*计算分配页面区间*/
sidx = start - bdata->node_min_pfn;
midx = max - bdata->node_min_pfn;
/*前一次分配的页号比这次开始分配的页面号大
那么,如果第一次没有分配到,回退到这次的
开始重新试,因为第一次分配是从上一次分配
的位置开始的*/
if (bdata->hint_idx > sidx) {
* Handle the valid case of sidx being zero and still
* catch the fallback below.
*/
fallback = sidx + 1;
/*从上一次分配的位置开始,对齐与页面*/
sidx = align_idx(bdata, bdata->hint_idx, step);
}
while (1) {
int merge;
void *region;
unsigned long eidx, i, start_off, end_off;
find_block:
/*查找第一个为0的位*/
sidx = find_next_zero_bit(bdata->node_bootmem_map, midx, sidx);
sidx = align_idx(bdata, sidx, step);
eidx = sidx + PFN_UP(size);/*结束位置*/
if (sidx >= midx || eidx > midx)/*找到结束了*/
break;
for (i = sidx; i < eidx; i++)/*检查这段区域是否空闲*/
if (test_bit(i, bdata->node_bootmem_map)) {/*如果不是,将跳过这段继续查找*/
sidx = align_idx(bdata, i, step);
if (sidx == i)
sidx += step;
goto find_block;
}
if (bdata->last_end_off & (PAGE_SIZE - 1) &&/*如果为相邻的页面,也就是说上次分配的页面和这次分配的开始页面为相邻的*/
PFN_DOWN(bdata->last_end_off) + 1 == sidx)
start_off = align_off(bdata, bdata->last_end_off, align);
else
start_off = PFN_PHYS(sidx);
/*merge==1表示上次结束和这次开始不在同一个页面上*/
merge = PFN_DOWN(start_off) < sidx;
end_off = start_off + size;
/*更新数据*/
bdata->last_end_off = end_off;
bdata->hint_idx = PFN_UP(end_off);
/*
* Reserve the area now:
*/
/*设定新加入的页面为保留,就是将对应的映射位置1*/
if (__reserve(bdata, PFN_DOWN(start_off) + merge,
PFN_UP(end_off), BOOTMEM_EXCLUSIVE))
BUG();
/*对应开始地址的虚拟地址返回*/
region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
start_off);
memset(region, 0, size);/*分配的大小*/
/*
* The min_count is set to 0 so that bootmem allocated blocks
* are never reported as leaks.
*/
/*调试用*/
kmemleak_alloc(region, size, 0, 0);
return region;
}
if (fallback) {/*回退,重新查看*/
sidx = align_idx(bdata, fallback - 1, step);
fallback = 0;
goto find_block;
}
return NULL;
} [/code |
|