免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1338 | 回复: 1
打印 上一主题 下一主题

linux启动内存分配器 2.。。。。。。。。。 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-01-06 16:35 |只看该作者 |倒序浏览
linux启动内存分配器 2.。。。。。。。。。







static void __init link_bootmem(bootmem_data_t *bdata)  
{  
    struct list_head *iter;  
    /*添加到全局链表bdata_list中*/  
    list_for_each(iter, &bdata_list) {  
        bootmem_data_t *ent;  
  
        ent = list_entry(iter, bootmem_data_t, list);  
        if (bdata->node_min_pfn < ent->node_min_pfn)  
            break;  
    }  
    list_add_tail(&bdata->list, iter);  
}  
/*
* link bdata in order
*/
/*添加到链表,由添加的代码可知
链表中的数据开始位置为递增的*/
static void __init link_bootmem(bootmem_data_t *bdata)
{
        struct list_head *iter;
        /*添加到全局链表bdata_list中*/
        list_for_each(iter, &bdata_list) {
                bootmem_data_t *ent;

                ent = list_entry(iter, bootmem_data_t, list);
                if (bdata->node_min_pfn < ent->node_min_pfn)
                        break;
        }
        list_add_tail(&bdata->list, iter);
}view plaincopy to clipboardprint?/**
* free_bootmem_with_active_regions - Call free_bootmem_node for each active range
* @nid: The node to free memory on. If MAX_NUMNODES, all nodes are freed.
* @max_low_pfn: The highest PFN that will be passed to free_bootmem_node
*
* If an architecture guarantees that all ranges registered with
* add_active_ranges() contain no holes and may be freed, this
* this function may be used instead of calling free_bootmem() manually.
*/  
/*用active_region来初始化bootmem分配器,基于低端内存区*/  
void __init free_bootmem_with_active_regions(int nid,  
                        unsigned long max_low_pfn)  
{  
    int i;  
    /*对每个节点上得活动内存区*/  
    for_each_active_range_index_in_nid(i, nid) {  
        unsigned long size_pages = 0;  
        unsigned long end_pfn = early_node_map.end_pfn;  
  
        if (early_node_map.start_pfn >= max_low_pfn)  
            continue;  
  
        if (end_pfn > max_low_pfn)  
            end_pfn = max_low_pfn;  
        /*计算活动区的页面数*/  
        size_pages = end_pfn - early_node_map.start_pfn;  
        /*释放这部分内存,起始就是对应位图值0*/  
        free_bootmem_node(NODE_DATA(early_node_map.nid),  
                PFN_PHYS(early_node_map.start_pfn),  
                size_pages << PAGE_SHIFT);  
    }  
}  
/**
* free_bootmem_with_active_regions - Call free_bootmem_node for each active range
* @nid: The node to free memory on. If MAX_NUMNODES, all nodes are freed.
* @max_low_pfn: The highest PFN that will be passed to free_bootmem_node
*
* If an architecture guarantees that all ranges registered with
* add_active_ranges() contain no holes and may be freed, this
* this function may be used instead of calling free_bootmem() manually.
*/
/*用active_region来初始化bootmem分配器,基于低端内存区*/
void __init free_bootmem_with_active_regions(int nid,
                                                unsigned long max_low_pfn)
{
        int i;
        /*对每个节点上得活动内存区*/
        for_each_active_range_index_in_nid(i, nid) {
                unsigned long size_pages = 0;
                unsigned long end_pfn = early_node_map.end_pfn;

                if (early_node_map.start_pfn >= max_low_pfn)
                        continue;

                if (end_pfn > max_low_pfn)
                        end_pfn = max_low_pfn;
                /*计算活动区的页面数*/
                size_pages = end_pfn - early_node_map.start_pfn;
                /*释放这部分内存,起始就是对应位图值0*/
                free_bootmem_node(NODE_DATA(early_node_map.nid),
                                PFN_PHYS(early_node_map.start_pfn),
                                size_pages << PAGE_SHIFT);
        }
}view plaincopy to clipboardprint?/**
* free_bootmem_node - mark a page range as usable
* @pgdat: node the range resides on
* @physaddr: starting address of the range
* @size: size of the range in bytes
*
* Partial pages will be considered reserved and left as they are.
*
* The range must reside completely on the specified node.
*/  
void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,  
                  unsigned long size)  
{  
    unsigned long start, end;  
    /*相关宏进行控制,调试用*/  
    kmemleak_free_part(__va(physaddr), size);  
  
    start = PFN_UP(physaddr);/*取上界*/  
    end = PFN_DOWN(physaddr + size);/*取下界*/  
  
      
    /*调用此函数对相关bit位清0,表示没有分配,这里保留位为0*/  
    mark_bootmem_node(pgdat->bdata, start, end, 0, 0);  
}  
/**
* free_bootmem_node - mark a page range as usable
* @pgdat: node the range resides on
* @physaddr: starting address of the range
* @size: size of the range in bytes
*
* Partial pages will be considered reserved and left as they are.
*
* The range must reside completely on the specified node.
*/
void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
                              unsigned long size)
{
        unsigned long start, end;
        /*相关宏进行控制,调试用*/
        kmemleak_free_part(__va(physaddr), size);

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

       
        /*调用此函数对相关bit位清0,表示没有分配,这里保留位为0*/
        mark_bootmem_node(pgdat->bdata, start, end, 0, 0);
}view plaincopy to clipboardprint?static int __init mark_bootmem_node(bootmem_data_t *bdata,  
                unsigned long start, unsigned long end,  
                int reserve, int flags)  
{  
    unsigned long sidx, eidx;  
  
    bdebug("nid=%td start=%lx end=%lx reserve=%d flags=%x\n",  
        bdata - bootmem_node_data, start, end, reserve, flags);  
  
    BUG_ON(start < bdata->node_min_pfn);  
    BUG_ON(end > bdata->node_low_pfn);  
    /*此两个变量为到节点最小内存页面的偏移量*/  
    sidx = start - bdata->node_min_pfn;  
    eidx = end - bdata->node_min_pfn;  
  
    if (reserve)/*如果设置了保留位*/  
        return __reserve(bdata, sidx, eidx, flags);  
    else/*相关的map位清0*/  
        __free(bdata, sidx, eidx);  
    return 0;  
}  
static int __init mark_bootmem_node(bootmem_data_t *bdata,
                                unsigned long start, unsigned long end,
                                int reserve, int flags)
{
        unsigned long sidx, eidx;

        bdebug("nid=%td start=%lx end=%lx reserve=%d flags=%x\n",
                bdata - bootmem_node_data, start, end, reserve, flags);

        BUG_ON(start < bdata->node_min_pfn);
        BUG_ON(end > bdata->node_low_pfn);
        /*此两个变量为到节点最小内存页面的偏移量*/
        sidx = start - bdata->node_min_pfn;
        eidx = end - bdata->node_min_pfn;

        if (reserve)/*如果设置了保留位*/
                return __reserve(bdata, sidx, eidx, flags);
        else/*相关的map位清0*/
                __free(bdata, sidx, eidx);
        return 0;
}view plaincopy to clipboardprint?/*bootmem分配器的保留操作*/  
static int __init __reserve(bootmem_data_t *bdata, unsigned long sidx,  
            unsigned long eidx, int flags)  
{  
    unsigned long idx;  
    int exclusive = flags & BOOTMEM_EXCLUSIVE;  
  
    bdebug("nid=%td start=%lx end=%lx flags=%x\n",  
        bdata - bootmem_node_data,  
        sidx + bdata->node_min_pfn,  
        eidx + bdata->node_min_pfn,  
        flags);  
    /*对连续的几个页面设置为保留*/  
    for (idx = sidx; idx < eidx; idx++)  
        if (test_and_set_bit(idx, bdata->node_bootmem_map)) {  
            if (exclusive) {  
                __free(bdata, sidx, idx);  
                return -EBUSY;  
            }  
            bdebug("silent double reserve of PFN %lx\n",  
                idx + bdata->node_min_pfn);  
        }  
    return 0;  
}  
/*bootmem分配器的保留操作*/
static int __init __reserve(bootmem_data_t *bdata, unsigned long sidx,
                        unsigned long eidx, int flags)
{
        unsigned long idx;
        int exclusive = flags & BOOTMEM_EXCLUSIVE;

        bdebug("nid=%td start=%lx end=%lx flags=%x\n",
                bdata - bootmem_node_data,
                sidx + bdata->node_min_pfn,
                eidx + bdata->node_min_pfn,
                flags);
        /*对连续的几个页面设置为保留*/
        for (idx = sidx; idx < eidx; idx++)
                if (test_and_set_bit(idx, bdata->node_bootmem_map)) {
                        if (exclusive) {
                                __free(bdata, sidx, idx);
                                return -EBUSY;
                        }
                        bdebug("silent double reserve of PFN %lx\n",
                                idx + bdata->node_min_pfn);
                }
        return 0;
}view plaincopy to clipboardprint?/*bootmem分配器中释放内存*/  
static void __init __free(bootmem_data_t *bdata,  
            unsigned long sidx, unsigned long eidx)  
{  
    unsigned long idx;  
  
    bdebug("nid=%td start=%lx end=%lx\n", bdata - bootmem_node_data,  
        sidx + bdata->node_min_pfn,  
        eidx + bdata->node_min_pfn);  
  
    if (bdata->hint_idx > sidx)  
        bdata->hint_idx = sidx;/*更新变量hint_idx,用于分配*/  
  
    for (idx = sidx; idx < eidx; idx++)/*对应位清0*/  
        if (!test_and_clear_bit(idx, bdata->node_bootmem_map))  
            BUG();  
}  
/*bootmem分配器中释放内存*/
static void __init __free(bootmem_data_t *bdata,
                        unsigned long sidx, unsigned long eidx)
{
        unsigned long idx;

        bdebug("nid=%td start=%lx end=%lx\n", bdata - bootmem_node_data,
                sidx + bdata->node_min_pfn,
                eidx + bdata->node_min_pfn);

        if (bdata->hint_idx > sidx)
                bdata->hint_idx = sidx;/*更新变量hint_idx,用于分配*/

        for (idx = sidx; idx < eidx; idx++)/*对应位清0*/
                if (!test_and_clear_bit(idx, bdata->node_bootmem_map))
                        BUG();
}view plaincopy to clipboardprint?void __init early_res_to_bootmem(u64 start, u64 end)  
{  
    int i, count;  
    u64 final_start, final_end;  
  
    count  = 0;  
    for (i = 0; i < MAX_EARLY_RES && early_res.end; i++)  
        count++;/*计算保留块的个数*/  
  
    printk(KERN_INFO "(%d early reservations) ==> bootmem [%010llx - %010llx]\n",  
             count, start, end);  
    for (i = 0; i < count; i++) {  
        struct early_res *r = &early_res;  
        printk(KERN_INFO "  #%d [%010llx - %010llx] %16s", i,  
            r->start, r->end, r->name);  
        final_start = max(start, r->start);  
        final_end = min(end, r->end);  
        if (final_start >= final_end) {  
            printk(KERN_CONT "\n");  
            continue;  
        }  
        printk(KERN_CONT " ==> [%010llx - %010llx]\n",  
            final_start, final_end);  
        /*将指定区间置为保留*/  
        reserve_bootmem_generic(final_start, final_end - final_start,  
                BOOTMEM_DEFAULT);  
    }  
}  
void __init early_res_to_bootmem(u64 start, u64 end)
{
        int i, count;
        u64 final_start, final_end;

        count  = 0;
        for (i = 0; i < MAX_EARLY_RES && early_res.end; i++)
                count++;/*计算保留块的个数*/

        printk(KERN_INFO "(%d early reservations) ==> bootmem [%010llx - %010llx]\n",
                         count, start, end);
        for (i = 0; i < count; i++) {
                struct early_res *r = &early_res;
                printk(KERN_INFO "  #%d [%010llx - %010llx] %16s", i,
                        r->start, r->end, r->name);
                final_start = max(start, r->start);
                final_end = min(end, r->end);
                if (final_start >= final_end) {
                        printk(KERN_CONT "\n");
                        continue;
                }
                printk(KERN_CONT " ==> [%010llx - %010llx]\n",
                        final_start, final_end);
                /*将指定区间置为保留*/
                reserve_bootmem_generic(final_start, final_end - final_start,
                                BOOTMEM_DEFAULT);
        }
}[/code]上面的保留指定区间reserve_bootmem_generic()函数最终调用如下函数
  1. view plaincopy to clipboardprint?/**
  2. * reserve_bootmem - mark a page range as usable
  3. * @addr: starting address of the range
  4. * @size: size of the range in bytes
  5. * @flags: reservation flags (see linux/bootmem.h)
  6. *
  7. * Partial pages will be reserved.
  8. *
  9. * The range must be contiguous but may span node boundaries.
  10. */  
  11. int __init reserve_bootmem(unsigned long addr, unsigned long size,  
  12.                 int flags)  
  13. {  
  14.     unsigned long start, end;  
  15.   
  16.     start = PFN_DOWN(addr);/*下界*/  
  17.     end = PFN_UP(addr + size);/*上界*/  
  18.   
  19.     return mark_bootmem(start, end, 1, flags);  
  20. }  
  21. /**
  22. * reserve_bootmem - mark a page range as usable
  23. * @addr: starting address of the range
  24. * @size: size of the range in bytes
  25. * @flags: reservation flags (see linux/bootmem.h)
  26. *
  27. * Partial pages will be reserved.
  28. *
  29. * The range must be contiguous but may span node boundaries.
  30. */
  31. int __init reserve_bootmem(unsigned long addr, unsigned long size,
  32.                             int flags)
  33. {
  34.         unsigned long start, end;

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

  37.         return mark_bootmem(start, end, 1, flags);
  38. }view plaincopy to clipboardprint?/*保留指定内存区间*/  
  39. static int __init mark_bootmem(unsigned long start, unsigned long end,  
  40.                 int reserve, int flags)  
  41. {  
  42.     unsigned long pos;  
  43.     bootmem_data_t *bdata;  
  44.   
  45.     pos = start;  
  46.     /*通过bdata_list链表找到在指定区间的bdata*/  
  47.     list_for_each_entry(bdata, &bdata_list, list) {  
  48.         int err;  
  49.         unsigned long max;  
  50.   
  51.         if (pos < bdata->node_min_pfn ||  
  52.             pos >= bdata->node_low_pfn) {  
  53.             BUG_ON(pos != start);  
  54.             continue;  
  55.         }  
  56.   
  57.         max = min(bdata->node_low_pfn, end);  
  58.         /*设置为保留*/  
  59.         err = mark_bootmem_node(bdata, pos, max, reserve, flags);  
  60.         if (reserve && err) {/*如果出错,递归调用*/  
  61.             mark_bootmem(start, pos, 0, 0);  
  62.             return err;  
  63.         }  
  64.   
  65.         if (max == end)  
  66.             return 0;  
  67.         pos = bdata->node_low_pfn;  
  68.     }  
  69.     BUG();  
  70. }  
  71. /*保留指定内存区间*/
  72. static int __init mark_bootmem(unsigned long start, unsigned long end,
  73.                                 int reserve, int flags)
  74. {
  75.         unsigned long pos;
  76.         bootmem_data_t *bdata;

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

  82.                 if (pos < bdata->node_min_pfn ||
  83.                     pos >= bdata->node_low_pfn) {
  84.                         BUG_ON(pos != start);
  85.                         continue;
  86.                 }

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

  94.                 if (max == end)
  95.                         return 0;
  96.                 pos = bdata->node_low_pfn;
  97.         }
  98.         BUG();
  99. }
复制代码
三、内存的分配和释放

介绍了上面的初始化流程,对于分配和释放就简单了,分配就是将分配器映射位图中对应的位置1,释放过程相反。
  1. view plaincopy to clipboardprint?/*分配size大小的空间*/  
  2. static void * __init alloc_bootmem_core(struct bootmem_data *bdata,  
  3.                     unsigned long size, unsigned long align,  
  4.                     unsigned long goal, unsigned long limit)  
  5. {  
  6.     unsigned long fallback = 0;  
  7.     unsigned long min, max, start, sidx, midx, step;  
  8.   
  9.     bdebug("nid=%td size=%lx [%lu pages] align=%lx goal=%lx limit=%lx\n",  
  10.         bdata - bootmem_node_data, size, PAGE_ALIGN(size) >> PAGE_SHIFT,  
  11.         align, goal, limit);  
  12.   
  13.     BUG_ON(!size);  
  14.     BUG_ON(align & (align - 1));  
  15.     BUG_ON(limit && goal + size > limit);  
  16.     /*如果没有映射位图返回空,分配失败*/  
  17.     if (!bdata->node_bootmem_map)  
  18.         return NULL;  
  19.   
  20.     min = bdata->node_min_pfn;  
  21.     max = bdata->node_low_pfn;  
  22.   
  23.     goal >>= PAGE_SHIFT;  
  24.     limit >>= PAGE_SHIFT;  
  25.   
  26.     if (limit && max > limit)  
  27.         max = limit;  
  28.     if (max <= min)  
  29.         return NULL;  
  30.     /*step为需要对齐于页面数*/  
  31.     step = max(align >> PAGE_SHIFT, 1UL);  
  32.     /*计算起始页面*/  
  33.     if (goal && min < goal && goal < max)  
  34.         start = ALIGN(goal, step);  
  35.     else  
  36.         start = ALIGN(min, step);  
  37.     /*计算分配页面区间*/  
  38.     sidx = start - bdata->node_min_pfn;  
  39.     midx = max - bdata->node_min_pfn;  
  40.     /*前一次分配的页号比这次开始分配的页面号大
  41.     那么,如果第一次没有分配到,回退到这次的
  42.     开始重新试,因为第一次分配是从上一次分配
  43.     的位置开始的*/  
  44.     if (bdata->hint_idx > sidx) {      
  45.          * Handle the valid case of sidx being zero and still  
  46.          * catch the fallback below.  
  47.          */  
  48.         fallback = sidx + 1;  
  49.         /*从上一次分配的位置开始,对齐与页面*/  
  50.         sidx = align_idx(bdata, bdata->hint_idx, step);  
  51.     }  
  52.   
  53.     while (1) {  
  54.         int merge;  
  55.         void *region;  
  56.         unsigned long eidx, i, start_off, end_off;  
  57. find_block:  
  58.         /*查找第一个为0的位*/  
  59.         sidx = find_next_zero_bit(bdata->node_bootmem_map, midx, sidx);  
  60.         sidx = align_idx(bdata, sidx, step);  
  61.         eidx = sidx + PFN_UP(size);/*结束位置*/  
  62.   
  63.         if (sidx >= midx || eidx > midx)/*找到结束了*/  
  64.             break;  
  65.   
  66.         for (i = sidx; i < eidx; i++)/*检查这段区域是否空闲*/  
  67.             if (test_bit(i, bdata->node_bootmem_map)) {/*如果不是,将跳过这段继续查找*/  
  68.                 sidx = align_idx(bdata, i, step);  
  69.                 if (sidx == i)  
  70.                     sidx += step;  
  71.                 goto find_block;  
  72.             }  
  73.   
  74.         if (bdata->last_end_off & (PAGE_SIZE - 1) &&/*如果为相邻的页面,也就是说上次分配的页面和这次分配的开始页面为相邻的*/  
  75.                 PFN_DOWN(bdata->last_end_off) + 1 == sidx)  
  76.             start_off = align_off(bdata, bdata->last_end_off, align);  
  77.         else  
  78.             start_off = PFN_PHYS(sidx);  
  79.   
  80.         /*merge==1表示上次结束和这次开始不在同一个页面上*/  
  81.         merge = PFN_DOWN(start_off) < sidx;  
  82.         end_off = start_off + size;  
  83.         /*更新数据*/  
  84.         bdata->last_end_off = end_off;  
  85.         bdata->hint_idx = PFN_UP(end_off);  
  86.   
  87.         /*
  88.          * Reserve the area now:
  89.          */  
  90.          /*设定新加入的页面为保留,就是将对应的映射位置1*/  
  91.         if (__reserve(bdata, PFN_DOWN(start_off) + merge,  
  92.                 PFN_UP(end_off), BOOTMEM_EXCLUSIVE))  
  93.             BUG();  
  94.         /*对应开始地址的虚拟地址返回*/  
  95.         region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +  
  96.                 start_off);  
  97.         memset(region, 0, size);/*分配的大小*/  
  98.         /*
  99.          * The min_count is set to 0 so that bootmem allocated blocks
  100.          * are never reported as leaks.
  101.          */  
  102.          /*调试用*/  
  103.         kmemleak_alloc(region, size, 0, 0);  
  104.         return region;  
  105.     }  
  106.   
  107.     if (fallback) {/*回退,重新查看*/  
  108.         sidx = align_idx(bdata, fallback - 1, step);  
  109.         fallback = 0;  
  110.         goto find_block;  
  111.     }  
  112.   
  113.     return NULL;  
  114. }  
复制代码

论坛徽章:
0
2 [报告]
发表于 2012-01-06 16:36 |只看该作者
谢谢分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP