我们看下boot传入的命令为:
"noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 rootfstype=cramfs mem=64M"
进入setup_arch函数后,首先遇到的和内存管理有关的,是parse_cmdline函数。
在arch\arm\kernel\setup.c中,mem命令参数被预执行。(关于其他__early_param参数,可以在SI中以__early_param为关键字搜索,其实也没几个。)
于是我们先跳进setup.c中分析一下这个函数。
执行完预操作之后,跟着就是paging_init对MMU的初始化
这个函数在arch\arm\mm\mmu.c中,我们先进入对应的位置分析。
paging_init这个函数虽然不大,但是调用的层次比较多,不过主要集中在arch\arm\mm\init.c和arch\arm\mm\mmu.c两个文件中,于是我们先尝试能不能把这两个文件给端了。
由于此时内存管理还未开始,所以这两个文件应该会比较独立先看init,而init中的内存管理,则是在mm\bootmem.c中。
在看代码之前,我们先看一个结构体:
typedef struct bootmem_data {
unsigned long node_boot_start; //可供内核管理的内存的起始物理地址
unsigned long node_low_pfn; //可管理物理内存结束位置的页框号
void *node_bootmem_map; //记录内存分配状况的位图的虚拟地址
unsigned long last_offset; //最后一次分配的页内偏移(结束地址)
unsigned long last_pos; //最后一次分配的PFN(结束页号)
unsigned long last_success; //最后成功申请的内存的起始物理地址,该地址之前的地址内存均被占用
struct list_head list; //挂到链表中的节点(该链表按node_boot_start成员由小到大排列)
} bootmem_data_t;
小结:
注意:bootmem_data_t操作的是物理内存,而不是虚拟内存!!!
1、 结构体bootmem_data_t描述了一块物理内存的范围及操作状态,它按其成员node_boot_start的大小在链表bdata_list中生序排列
2、 bootmem_data_t中内存的申请,实际只是将其成员*node_bootmem_map的位图标记为1。注意1位对应一页内存
3、 注意:这个文件中的函数是在系统启动的时候,还未初始化slab等伙伴系统时使用的,所以该文件中的释放函数,释放的是未占用的内存页给slab系统能继续使用,而已经标记占用的内存则不被释放
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
函数列表:
@@@@@
//计算有多少页,pages为字节数
unsigned long __init bootmem_bootmap_pages(unsigned long pages)
//在bdata描述的内存区中申请size大小的内存,并返回该内存的开始地址。注意,内存的地址要是连续的
void * __init __alloc_bootmem_core(
struct bootmem_data *bdata,
unsigned long size, //申请的大小
unsigned long align, //对齐字节
unsigned long goal, //开始地址(全局地址,如无则从bdata开始地址起)
unsigned long limit) //操作的最大地址
//标记从phyaddr开始,大小size的位图被占用
void __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
unsigned long size)
void __init reserve_bootmem(unsigned long addr, unsigned long size)
//释放物理内存管理数据(主要操作是清除占用的位图)
void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
unsigned long size)
void __init free_bootmem(unsigned long addr, unsigned long size)
//释放没有被占用的内存页(bootmem_data_t的map成员标记为空的内存页,以及保存bootmem结构体信息所在的页),返回释放了多少页内存
unsigned long __init free_all_bootmem_node(pg_data_t *pgdat)
unsigned long __init free_all_bootmem(void)
//初始化设置pg_data_t结构体中的bootmeme_data_t成员。具体实现是设置该成员的三个成员,并标记内存位图该区域内存已被占用
unsigned long __init init_bootmem(unsigned long start, unsigned long pages)
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//这个存在于arch/arm/mm/mmzone.c中,是给后面几个用宏NODE_DATA(0)的函数用的
static bootmem_data_t node_bootmem_data[MAX_NUMNODES];
MAX_NUMNODES = 1;
#define NODE_DATA(nid) (&discontig_node_data[nid])
pg_data_t discontig_node_data[MAX_NUMNODES] = {
{ .bdata = &node_bootmem_data[0] },
{ .bdata = &node_bootmem_data[1] },
{ .bdata = &node_bootmem_data[2] },
{ .bdata = &node_bootmem_data[3] },
#if MAX_NUMNODES == 16
{ .bdata = &node_bootmem_data[4] },
{ .bdata = &node_bootmem_data[5] },
{ .bdata = &node_bootmem_data[6] },
{ .bdata = &node_bootmem_data[7] },
{ .bdata = &node_bootmem_data[8] },
{ .bdata = &node_bootmem_data[9] },
{ .bdata = &node_bootmem_data[10] },
{ .bdata = &node_bootmem_data[11] },
{ .bdata = &node_bootmem_data[12] },
{ .bdata = &node_bootmem_data[13] },
{ .bdata = &node_bootmem_data[14] },
{ .bdata = &node_bootmem_data[15] },
#endif
};
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
static LIST_HEAD(bdata_list);
//定义一个空链表,里面的成员为bootmem_data_t,并按其成员node_boot_start由小到大排列。
//分析完整个文件后,发现这个链表实际是将整个内存区域给链在了一起,如果两块64M的内存,那么每一块用bootmem_data_t结构体来描述,然后这个链表将这两个描述链在一起
//计算管理pages页内存需要多少页内存的位图来映射
unsigned long __init bootmem_bootmap_pages(unsigned long pages)
{
unsigned long mapsize;
mapsize = (pages+7)/8; //对8取整(补充,这里对8取整是为了调用者的操作——操作的是字节位图,一个字节8位,见init_bootmem_core最后的操作)
mapsize = (mapsize + ~PAGE_MASK) & PAGE_MASK; //对4K每页取整
mapsize >>= PAGE_SHIFT; //计算有多少页
return mapsize;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//将成员bdata挂入全局链表bdata_list中,按node_boot_start自动排序
static void __init link_bootmem(bootmem_data_t *bdata)
{
bootmem_data_t *ent;
if (list_empty(&bdata_list)) { //链表为空
list_add(&bdata->list, &bdata_list); //加入第一个节点后返回
return;
}
//遍历链表中的所有节点
list_for_each_entry(ent, &bdata_list, list) {
//开始地址比找到的节点小
if (bdata->node_boot_start < ent->node_boot_start) {
list_add_tail(&bdata->list, &ent->list); //将节点挂在本节点前,返回
return;
}
}
//如果所有节点的地址都大于bdata,将节点挂到链表的最后
list_add_tail(&bdata->list, &bdata_list);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//计算物理内存所占的页大小(8页对齐)
static unsigned long __init get_mapsize(bootmem_data_t *bdata)
{
unsigned long mapsize;
unsigned long start = PFN_DOWN(bdata->node_boot_start); //起始物理地址页号
unsigned long end = bdata->node_low_pfn; //可管理物理内存结束位置的页框号
mapsize = ((end - start) + 7) / 8; //大小按8页对齐(补充,这里按8页对齐是为了调用者的操作——操作的是字节位图,一个字节8位,见后一个函数)
return ALIGN(mapsize, sizeof(long));
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//初始化设置pg_data_t结构体中的bootmeme_data_t成员。具体实现是设置该成员的三个成员,并标记内存位图该区域内存已被占用
static unsigned long __init init_bootmem_core(
pg_data_t *pgdat,
unsigned long mapstart, //页框号(物理地址) – 管理的位图地址
unsigned long start, //内存管理的起始物理地址页框号
unsigned long end) //内存管理的结束物理地址页框号
{
bootmem_data_t *bdata = pgdat->bdata; //获取bootmem_data_t信息
unsigned long mapsize;
// PFN_PHYS – 页框转为物理地址
// phys_to_virt – 物理地址转为虚拟地址
//映射被管理内存的位图
bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart));
bdata->node_boot_start = PFN_PHYS(start); //描述内存块的起始物理地址
bdata->node_low_pfn = end; //描述内存块的结束物理地址页框号
link_bootmem(bdata); //将节点按boot_start升序挂入bdata_list链表
mapsize = get_mapsize(bdata); //获取管理内存的范围(页)
memset(bdata->node_bootmem_map, 0xff, mapsize); //全写1,标记该区域内存被占用
return mapsize;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//标记从addr开始,大小size的位图被占用
static void __init reserve_bootmem_core(
bootmem_data_t *bdata, //句柄
unsigned long addr, //开始地址
unsigned long size) //大小
{
unsigned long sidx, eidx;
unsigned long i;
//出错情况:大小为0,地址高于边界,操作区域高于边界
BUG_ON(!size);
BUG_ON(PFN_DOWN(addr) >= bdata->node_low_pfn);
BUG_ON(PFN_UP(addr + size) > bdata->node_low_pfn);
sidx = PFN_DOWN(addr - bdata->node_boot_start); //计算相对开始的偏移地址
eidx = PFN_UP(addr + size - bdata->node_boot_start); //计算结束地址
//将该区域的位图设置为1(占用)
for (i = sidx; i < eidx; i++)
test_and_set_bit(i, bdata->node_bootmem_map);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//释放物理内存管理数据(主要操作是清除占用的位图)
static void __init free_bootmem_core(
bootmem_data_t *bdata,
unsigned long addr, //开始的物理地址
unsigned long size) //大小
{
unsigned long sidx, eidx;
unsigned long i;
BUG_ON(!size); //要操作的大小为0,出错
BUG_ON(PFN_DOWN(addr + size) > bdata->node_low_pfn);
//last_success,在该地址之前的内存都已被分配出去,所以内存申请的时候会以这个地址为起点找新的内存,而释放的时候,如果地址小于这个地址,则更新这个地址,以表明这个地址之后有内存可以被申请
if (addr < bdata->last_success) bdata->last_success = addr;
//计算开始页号和结束页号
sidx = PFN_UP(addr) - PFN_DOWN(bdata->node_boot_start);
eidx = PFN_DOWN(addr + size - bdata->node_boot_start);
//清除占用的位图
for (i = sidx; i < eidx; i++) {
if (unlikely(!test_and_clear_bit(i, bdata->node_bootmem_map)))
BUG();
}
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//在bdata描述的内存区中申请size大小的内存,并返回该内存的开始地址。注意,内存的地址要是连续的
void * __init __alloc_bootmem_core(
struct bootmem_data *bdata,
unsigned long size, //申请的大小
unsigned long align, //对齐字节
unsigned long goal, //全局开始地址
unsigned long limit) //地址的最大值
{
unsigned long offset, remaining_size, areasize, preferred;
unsigned long i, start = 0, incr, eidx, end_pfn;
void *ret;
if (!size) { //大小为0,出错
printk("__alloc_bootmem_core(): zero-sized request\n");
BUG();
}
BUG_ON(align & (align-1)); //对起字节错误
//如果最大地址设置不为0,当开始地址大于最大值时,出错
if (limit && bdata->node_boot_start >= limit) return NULL;
//位图地址为空,本块物理内存是空的
if (!bdata->node_bootmem_map) return NULL;
end_pfn = bdata->node_low_pfn; //本块物理地址的结束地址页
limit = PFN_DOWN(limit); //最大限制的地址页
if (limit && end_pfn > limit) end_pfn = limit; //修正结束地址为最大限制地址
//计算操作范围(单位:页)
eidx = end_pfn - PFN_DOWN(bdata->node_boot_start);
//有对齐且开始物理地址没对齐à计算从开始地址的偏移页
offset = 0;
if (align && (bdata->node_boot_start & (align - 1UL)) != 0)
offset = align - (bdata->node_boot_start & (align - 1UL));
offset = PFN_DOWN(offset);
//goal存在且范围在操作范围
if (goal && goal >= bdata->node_boot_start && PFN_DOWN(goal) < end_pfn)
{
preferred = goal - bdata->node_boot_start; //计算从开始地址开始要跳过的内存
//偏移地址在上一次的申请地址之前
if (bdata->last_success >= preferred)
//没有最大地址限制或最大地址大于最后成功地址,更新pre地址为从上一次成功地址开始找
if (!limit || (limit && limit > bdata->last_success))
preferred = bdata->last_success;
}
else preferred = 0;
//pre地址对齐后加偏移地址
preferred = PFN_DOWN(ALIGN(preferred, align)) + offset;
areasize = (size + PAGE_SIZE-1) / PAGE_SIZE; //需要申请的大小(页)
incr = align >> PAGE_SHIFT ? : 1; //一次加几页
//扫描范围内连续空闲的块
restart_scan:
for (i = preferred; i < eidx; i += incr) { //eidx – 操作的最大范围
unsigned long j;
//从i开始找到为0的位
i = find_next_zero_bit(bdata->node_bootmem_map, eidx, i);
i = ALIGN(i, incr); //i对incr对齐
if (i >= eidx) break; //超过最大地址,退出
if (test_bit(i, bdata->node_bootmem_map)) //该位已经申请内存,继续下一个循环
continue;
//从i开始,是否连续areasize大小的内存都是空闲的
for (j = i + 1; j < i + areasize; ++j) { //areasize – 需要申请的大小
if (j >= eidx) goto fail_block; //超过最大地址,出错
if (test_bit(j, bdata->node_bootmem_map)) //已经申请了,出错
goto fail_block;
}
//从i开始找到大小为areasize的空闲地址
start = i;
goto found;
fail_block:
i = ALIGN(j, incr); //出错,调整地址对齐
} //for(;;)
//没有找到地址
//如果pre大于offset偏移,则从offset重新开始找一次
if (preferred > offset) {
preferred = offset;
goto restart_scan;
}
//返回NULL,没有找到足够大的内存
return NULL;
//找到足够大的内存
found:
//最后操作的成功地址更新为start(开始申请内存的地址)
bdata->last_success = PFN_PHYS(start);
BUG_ON(start >= eidx); //地址超过范围,出错
//更新最后一页,页内偏移,并计算返回地址
if (align < PAGE_SIZE && //对齐大小不足一页且
bdata->last_offset && //上次分配有页内偏移且
bdata->last_pos+1 == start) //本次开始页紧挨着上次分配页
{
//尝试先使用页内的剩余内存
offset = ALIGN(bdata->last_offset, align); //偏移修改为上一次的偏移,并对齐
BUG_ON(offset > PAGE_SIZE);
remaining_size = PAGE_SIZE - offset; //本页还剩多少空间
if (size < remaining_size) //要分配的大小页内能分配完
{
areasize = 0; //不需要申请新的内存空间
bdata->last_offset = offset + size; //更新最后一次分配的结束地址
ret = phys_to_virt(bdata->last_pos * PAGE_SIZE +
offset +
bdata->node_boot_start); //计算返回的地址
}
else //要分配的大小页内剩余的空间装不下
{
//重新计算还需要申请多少页
remaining_size = size - remaining_size;
areasize = (remaining_size + PAGE_SIZE-1) / PAGE_SIZE;
ret = phys_to_virt(bdata->last_pos * PAGE_SIZE +
offset +
bdata->node_boot_start); //计算返回的地址
bdata->last_pos = start + areasize - 1; //更新最后一页的页号
bdata->last_offset = remaining_size; //计算偏移
}
bdata->last_offset &= ~PAGE_MASK; //将偏移mask为页内
}
else
{
//更新最后一页,页内偏移,并计算返回地址
bdata->last_pos = start + areasize - 1;
bdata->last_offset = size & ~PAGE_MASK;
ret = phys_to_virt(start * PAGE_SIZE + bdata->node_boot_start);
}
//标记内存被占用
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; //返回地址
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//释放没有被占用的内存页(bootmem_data_t的map成员标记为空的内存页,以及保存bootmem结构体信息所在的页),返回释放了多少页内存
//这个函数之后,内存交由SLUB管理
static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat)
{
struct page *page;
unsigned long pfn;
bootmem_data_t *bdata = pgdat->bdata; //物理内存
unsigned long i, count, total = 0;
unsigned long idx;
unsigned long *map;
int gofast = 0;
BUG_ON(!bdata->node_bootmem_map); //无管理位图
count = 0;
pfn = PFN_DOWN(bdata->node_boot_start); //开始页号
idx = bdata->node_low_pfn - pfn; //包含多少页
map = bdata->node_bootmem_map; //内存映射位图
//检查物理地址是否对齐O(LOG2(BITS_PER_LONG))
if (bdata->node_boot_start == 0 ||
ffs(bdata->node_boot_start) - PAGE_SHIFT > ffs(BITS_PER_LONG))
gofast = 1;
//遍历描述符所描述的整个内存空间
for (i = 0; i < idx; ) {
unsigned long v = ~map[i / BITS_PER_LONG]; //取对应的位图
//开始地址对齐且内存全空
if (gofast && v == ~0UL)
{
int order;
page = pfn_to_page(pfn);
count += BITS_PER_LONG; //累加处理的页数
order = ffs(BITS_PER_LONG) - 1; //5
__free_pages_bootmem(page, order); //释放内存
i += BITS_PER_LONG;
page += BITS_PER_LONG;
}
else if (v) //内存中有空的页
{
unsigned long m;
page = pfn_to_page(pfn); //从页框获取页地址
//在v中找到空的页,并释放它
for (m = 1; m && i < idx; m<<=1, page++, i++) {
if (v & m) {
count++;
__free_pages_bootmem(page, 0);
}
}
}
else //内存位图对应的内存区全满
{
i += BITS_PER_LONG;
}
pfn += BITS_PER_LONG;
}
total += count; //记录操作了多少页内存
page = virt_to_page(bdata->node_bootmem_map); //获取位图所在的页
count = 0;
//操作范围:结构体所在的页
idx = (get_mapsize(bdata) + PAGE_SIZE-1) >> PAGE_SHIFT;
for (i = 0; i < idx; i++, page++) {
__free_pages_bootmem(page, 0); //将该页释放
count++;
}
total += count;
bdata->node_bootmem_map = NULL; //挂空,说明无映射的内存
return total; //返回总共释放的页数
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//初始化bootmem节点
unsigned long __init init_bootmem_node(
pg_data_t *pgdat, //宿主
unsigned long freepfn, //bootmem_data所在的管理页
unsigned long startpfn, //内存开始页
unsigned long endpfn) //内存结束页
{
return init_bootmem_core(pgdat, freepfn, startpfn, endpfn);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//标记从phyaddr开始,大小size的位图被占用
void __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
unsigned long size)
{
reserve_bootmem_core(pgdat->bdata, physaddr, size);
}
//释放物理内存管理数据(主要操作是清除占用的位图)
void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
unsigned long size)
{
free_bootmem_core(pgdat->bdata, physaddr, size);
}
//释放全部内存(bootmem_data_t的map成员标记为空的内存页,以及保存bootmem结构体信息所在的页),返回释放了多少页内存
unsigned long __init free_all_bootmem_node(pg_data_t *pgdat)
{
return free_all_bootmem_core(pgdat);
}
//初始化设置pg_data_t结构体中的bootmeme_data_t成员。具体实现是设置该成员的三个成员,并标记内存位图该区域内存已被占用
unsigned long __init init_bootmem(unsigned long start, unsigned long pages)
{
max_low_pfn = pages; //由于从0开始,所以pages对应着最大页框
min_low_pfn = start; //管理地址在第一个页中
//句炳,位图,开始,结束
return init_bootmem_core(NODE_DATA(0), start, 0, pages);
}
/标记从addr开始,大小size的位图被占用
void __init reserve_bootmem(unsigned long addr, unsigned long size)
{
reserve_bootmem_core(NODE_DATA(0)->bdata, addr, size);
}
//释放物理内存管理数据(主要操作是清除占用的位图)
void __init free_bootmem(unsigned long addr, unsigned long size)
{
free_bootmem_core(NODE_DATA(0)->bdata, addr, size);
}
//释放全部内存(bootmem_data_t的map成员标记为空的内存页,以及保存bootmem结构体信息所在的页),返回释放了多少页内存
unsigned long __init free_all_bootmem(void)
{
return free_all_bootmem_core(NODE_DATA(0));
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//遍历每一块物理内存,申请内存
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) {
//在bdata描述的内存区中申请size大小的内存,并返回该内存的开始地址
ptr = __alloc_bootmem_core(bdata, size, align, goal, 0);
if (ptr) return ptr;
}
return NULL;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//同上
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; //申请成功
//申请失败
printk(KERN_ALERT "bootmem alloc of %lu bytes failed!\n", size);
panic("Out of memory");
return NULL;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//在指定区域申请内存,如果失败,则在整个内存区申请内存
void * __init __alloc_bootmem_node(
pg_data_t *pgdat,
unsigned long size,
unsigned long align,
unsigned long goal)
{
void *ptr;
//在指定内存区域申请内存
ptr = __alloc_bootmem_core(pgdat->bdata, size, align, goal, 0);
if (ptr) return ptr;
//申请失败,尝试在整个内存区域申请内存
//由于一般只有一块内存,所以这个函数作用同上
return __alloc_bootmem(size, align, goal);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//同上,只是指定了最大地址ARCH_LOW_ADDRESS_LIMIT
//在start_kernel初始化中有一次调用:
// alloc_bootmem_low_pages(PAGE_SIZE)
// #define alloc_bootmem_low_pages(x) __alloc_bootmem_low(x, PAGE_SIZE, 0)
void * __init __alloc_bootmem_low(
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,
ARCH_LOW_ADDRESS_LIMIT); //申请内存
if (ptr) return ptr; //成功,则返回
}
//失败,打印错误信息
printk(KERN_ALERT "low bootmem alloc of %lu bytes failed!\n", size);
panic("Out of low memory");
return NULL;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//在指定区域内申请内存
void * __init __alloc_bootmem_low_node(
pg_data_t *pgdat,
unsigned long size,
unsigned long align,
unsigned long goal)
{
return __alloc_bootmem_core(pgdat->bdata, size, align, goal,
ARCH_LOW_ADDRESS_LIMIT);
}