这是一个定义好的宏,用于分配内存:
#define
alloc_bootmem_pages(x) /
__alloc_bootmem(x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS))
其中MAX_DMA_ADDRESS的定义为:
#define
MAX_DMA_ADDRESS PAGE_OFFSET
而PAGE_OFFSET是定义成0的。
__pa的定义为:
#define
__pa(vaddr) virt_to_phys((void *)(vaddr))
virt_to_phys的定义为:
#define
virt_to_phys(vaddr) ((unsigned long) (vaddr))
实际上,alloc_bootmem_pages这个宏就变成了
#define
alloc_bootmem_pages(x) /
__alloc_bootmem(x, PAGE_SIZE, 0)
看看
__alloc_bootmem的实现代码:
void
* __init __alloc_bootmem(unsigned long size, unsigned long align,
unsigned long goal)
{
void *mem = __alloc_bootmem_nopanic(size,align,goal);
if (mem)
return mem;
/*
* Whoops, we cannot satisfy the allocation request.
*/
printk(KERN_ALERT "bootmem alloc of %lu bytes failed!/n", size);
panic("Out of memory");
return NULL;
}
再看看的
__alloc_bootmem_nopanic实现:
void
* __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align,
unsigned long goal)
{
bootmem_data_t *bdata;
void *ptr;
list_for_each_entry(bdata, &bdata_list, list) {
ptr = __alloc_bootmem_core(bdata, size, align, goal, 0);
if (ptr)
return ptr;
}
return NULL;
}
前面说过bdata_list实际上只有一个元素,因此这一循环将只执行一次,
__alloc_bootmem_core的实现在bootmem.c中,代码较长。先看看注释和声明:
/*
* We 'merge' subsequent allocations to save space. We might 'lose'
* some fraction of a page if allocations cannot be satisfied due to
* size constraints on boxes where there is physical RAM space
* fragmentation - in these cases (mostly large memory boxes) this
* is not a problem.
*
* On low memory boxes we get it right in 100% of the cases.
*
* alignment has to be a power of 2 value.
*
* NOTE: This function is _not_ reentrant.
*/
void
* __init
__alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size,
unsigned long align, unsigned long goal, unsigned long limit)
{
当调用到这里的时候goal和limit的值都为0,align为页面大小4K。
以下代码显示了__alloc_bootmem的页面分配策略:
restart_scan:
for (i = preferred; i < eidx; i += incr) {
unsigned long j;
i = find_next_zero_bit(bdata->node_bootmem_map, eidx, i);
i = ALIGN(i, incr);
if (i >= eidx)
break;
if (test_bit(i, bdata->node_bootmem_map))
continue;
for (j = i + 1; j < i + areasize; ++j) {
if (j >= eidx)
goto fail_block;
if (test_bit(j, bdata->node_bootmem_map))
goto fail_block;
}
start = i;
goto found;
fail_block:
i = ALIGN(j, incr);
}
在上面的代码中,prefeered为0,areasize表示总共要求分配的页数,incr取1。很简单,就是沿着页表从低往高找,找到第一个可用的页块。
当分配成功后,此函数将记录这次分配的信息:
found:
bdata->last_success = PFN_PHYS(start);
BUG_ON(start >= eidx);
/*
* Is the next page of the previous allocation-end the start
* of this allocation's buffer? If yes then we can 'merge'
* the previous partial page with this allocation.
*/
if (align < PAGE_SIZE &&
bdata->last_offset && bdata->last_pos+1 == start) {
…
} else {
bdata->last_pos = start + areasize - 1;
bdata->last_offset = size & ~PAGE_MASK;
ret = phys_to_virt(start * PAGE_SIZE + bdata->node_boot_start);
}
/*
* Reserve the area now:
*/
for (i = start; i < start + areasize; i++)
if (unlikely(test_and_set_bit(i, bdata->node_bootmem_map)))
BUG();
memset(ret, 0, size);
return ret;
还有一个值得注意的是在分配成功之后,此函数还负责将这段内存清0。