中关村村草 发表于 2012-01-05 16:28

linux启动内存分配器 3。。。。。。。。。。

linux启动内存分配器 3。。。。。。。。。。view plaincopy to clipboardprint?/**
* reserve_bootmem - mark a page range as usable
* @addr: starting address of the range
* @size: size of the range in bytes
* @flags: reservation flags (see linux/bootmem.h)
*
* Partial pages will be reserved.
*
* The range must be contiguous but may span node boundaries.
*/
int __init reserve_bootmem(unsigned long addr, unsigned long size,
                int flags)
{
    unsigned long start, end;

    start = PFN_DOWN(addr);/*下界*/
    end = PFN_UP(addr + size);/*上界*/

    return mark_bootmem(start, end, 1, flags);
}
/**
* reserve_bootmem - mark a page range as usable
* @addr: starting address of the range
* @size: size of the range in bytes
* @flags: reservation flags (see linux/bootmem.h)
*
* Partial pages will be reserved.
*
* The range must be contiguous but may span node boundaries.
*/
int __init reserve_bootmem(unsigned long addr, unsigned long size,
                          int flags)
{
        unsigned long start, end;

        start = PFN_DOWN(addr);/*下界*/
        end = PFN_UP(addr + size);/*上界*/

        return mark_bootmem(start, end, 1, flags);
}view plaincopy to clipboardprint?/*保留指定内存区间*/
static int __init mark_bootmem(unsigned long start, unsigned long end,
                int reserve, int flags)
{
    unsigned long pos;
    bootmem_data_t *bdata;

    pos = start;
    /*通过bdata_list链表找到在指定区间的bdata*/
    list_for_each_entry(bdata, &bdata_list, list) {
      int err;
      unsigned long max;

      if (pos < bdata->node_min_pfn ||
            pos >= bdata->node_low_pfn) {
            BUG_ON(pos != start);
            continue;
      }

      max = min(bdata->node_low_pfn, end);
      /*设置为保留*/
      err = mark_bootmem_node(bdata, pos, max, reserve, flags);
      if (reserve && err) {/*如果出错,递归调用*/
            mark_bootmem(start, pos, 0, 0);
            return err;
      }

      if (max == end)
            return 0;
      pos = bdata->node_low_pfn;
    }
    BUG();
}
/*保留指定内存区间*/
static int __init mark_bootmem(unsigned long start, unsigned long end,
                                int reserve, int flags)
{
        unsigned long pos;
        bootmem_data_t *bdata;

        pos = start;
        /*通过bdata_list链表找到在指定区间的bdata*/
        list_for_each_entry(bdata, &bdata_list, list) {
                int err;
                unsigned long max;

                if (pos < bdata->node_min_pfn ||
                  pos >= bdata->node_low_pfn) {
                        BUG_ON(pos != start);
                        continue;
                }

                max = min(bdata->node_low_pfn, end);
                /*设置为保留*/
                err = mark_bootmem_node(bdata, pos, max, reserve, flags);
                if (reserve && err) {/*如果出错,递归调用*/
                        mark_bootmem(start, pos, 0, 0);
                        return err;
                }

                if (max == end)
                        return 0;
                pos = bdata->node_low_pfn;
        }
        BUG();
}三、内存的分配和释放

介绍了上面的初始化流程,对于分配和释放就简单了,分配就是将分配器映射位图中对应的位置1,释放过程相反。


view plaincopy to clipboardprint?/*分配size大小的空间*/
static void * __init alloc_bootmem_core(struct bootmem_data *bdata,
                  unsigned long size, unsigned long align,
                  unsigned long goal, unsigned long limit)
{
    unsigned long fallback = 0;
    unsigned long min, max, start, sidx, midx, step;

    bdebug("nid=%td size=%lx [%lu pages] align=%lx goal=%lx limit=%lx\n",
      bdata - bootmem_node_data, size, PAGE_ALIGN(size) >> PAGE_SHIFT,
      align, goal, limit);

    BUG_ON(!size);
    BUG_ON(align & (align - 1));
    BUG_ON(limit && goal + size > limit);
    /*如果没有映射位图返回空,分配失败*/
    if (!bdata->node_bootmem_map)
      return NULL;

    min = bdata->node_min_pfn;
    max = bdata->node_low_pfn;

    goal >>= PAGE_SHIFT;
    limit >>= PAGE_SHIFT;

    if (limit && max > limit)
      max = limit;
    if (max <= min)
      return NULL;
    /*step为需要对齐于页面数*/
    step = max(align >> PAGE_SHIFT, 1UL);
    /*计算起始页面*/
    if (goal && min < goal && goal < max)
      start = ALIGN(goal, step);
    else
      start = ALIGN(min, step);
    /*计算分配页面区间*/
    sidx = start - bdata->node_min_pfn;
    midx = max - bdata->node_min_pfn;
    /*前一次分配的页号比这次开始分配的页面号大
    那么,如果第一次没有分配到,回退到这次的
    开始重新试,因为第一次分配是从上一次分配
    的位置开始的*/
    if (bdata->hint_idx > sidx) {      
         * Handle the valid case of sidx being zero and still
         * catch the fallback below.
         */
      fallback = sidx + 1;
      /*从上一次分配的位置开始,对齐与页面*/
      sidx = align_idx(bdata, bdata->hint_idx, step);
    }

    while (1) {
      int merge;
      void *region;
      unsigned long eidx, i, start_off, end_off;
find_block:
      /*查找第一个为0的位*/
      sidx = find_next_zero_bit(bdata->node_bootmem_map, midx, sidx);
      sidx = align_idx(bdata, sidx, step);
      eidx = sidx + PFN_UP(size);/*结束位置*/

      if (sidx >= midx || eidx > midx)/*找到结束了*/
            break;

      for (i = sidx; i < eidx; i++)/*检查这段区域是否空闲*/
            if (test_bit(i, bdata->node_bootmem_map)) {/*如果不是,将跳过这段继续查找*/
                sidx = align_idx(bdata, i, step);
                if (sidx == i)
                  sidx += step;
                goto find_block;
            }

      if (bdata->last_end_off & (PAGE_SIZE - 1) &&/*如果为相邻的页面,也就是说上次分配的页面和这次分配的开始页面为相邻的*/
                PFN_DOWN(bdata->last_end_off) + 1 == sidx)
            start_off = align_off(bdata, bdata->last_end_off, align);
      else
            start_off = PFN_PHYS(sidx);

      /*merge==1表示上次结束和这次开始不在同一个页面上*/
      merge = PFN_DOWN(start_off) < sidx;
      end_off = start_off + size;
      /*更新数据*/
      bdata->last_end_off = end_off;
      bdata->hint_idx = PFN_UP(end_off);

      /*
         * Reserve the area now:
         */
         /*设定新加入的页面为保留,就是将对应的映射位置1*/
      if (__reserve(bdata, PFN_DOWN(start_off) + merge,
                PFN_UP(end_off), BOOTMEM_EXCLUSIVE))
            BUG();
      /*对应开始地址的虚拟地址返回*/
      region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
                start_off);
      memset(region, 0, size);/*分配的大小*/
      /*
         * The min_count is set to 0 so that bootmem allocated blocks
         * are never reported as leaks.
         */
         /*调试用*/
      kmemleak_alloc(region, size, 0, 0);
      return region;
    }

    if (fallback) {/*回退,重新查看*/
      sidx = align_idx(bdata, fallback - 1, step);
      fallback = 0;
      goto find_block;
    }

    return NULL;
}[/code

第一夫人 发表于 2012-01-05 16:29

谢谢分享
页: [1]
查看完整版本: linux启动内存分配器 3。。。。。。。。。。