linux内存分析,Linux内存分析(2) -- mm\\bootmem.c-eydwyz-ChinaUnix博客

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

// #definealloc_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);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值