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

我们看下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_initMMU的初始化

这个函数在arch\arm\mm\mmu.c中,我们先进入对应的位置分析。

paging_init这个函数虽然不大,但是调用的层次比较多,不过主要集中在arch\arm\mm\init.carch\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_tmap成员标记为空的内存页,以及保存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);        //iincr对齐

              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_tmap成员标记为空的内存页,以及保存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_tmap成员标记为空的内存页,以及保存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_tmap成员标记为空的内存页,以及保存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);

}

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值