您的位置:首页 > 运维架构 > Linux

Linux内存分析(2) -- mm\bootmem.c

2012-04-02 00:05 411 查看

我们看下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);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: