免费注册 查看新帖 |

Chinaunix

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

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

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





linux启动内存分配器是在伙伴系统、slab机制实现之前,为满足内核中内存的分配而建立的。本身的机制比较简单,使用位图来进行标志分配和释放。

一、数据结构介绍

1,保留区间

因为在建立启动内存分配器的时候,会涉及保留内存。也就是说,之前保留给页表、分配器本身(用于映射的位图)、io等得内存在分配器建立后,当用它来分配内存空间时,保留出来的那些部分就不能再分配了。linux中对保留内存空间的部分用下列数据结构表示

  1. view plaincopy to clipboardprint?/*
  2. * Early reserved memory areas.
  3. */  
  4. #define MAX_EARLY_RES 20/*保留空间最大块数*/   
  5.   
  6. struct early_res {/*保留空间结构*/  
  7.     u64 start, end;  
  8.     char name[16];  
  9.     char overlap_ok;  
  10. };  
  11. /*保留内存空间全局变量*/  
  12. static struct early_res early_res[MAX_EARLY_RES] __initdata = {  
  13.     { 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */  
  14.     {}  
  15. };  
  16. /*
  17. * Early reserved memory areas.
  18. */
  19. #define MAX_EARLY_RES 20/*保留空间最大块数*/

  20. struct early_res {/*保留空间结构*/
  21.         u64 start, end;
  22.         char name[16];
  23.         char overlap_ok;
  24. };
  25. /*保留内存空间全局变量*/
  26. static struct early_res early_res[MAX_EARLY_RES] __initdata = {
  27.         { 0, PAGE_SIZE, "BIOS data page" },        /* BIOS data page */
  28.         {}
  29. };2,bootmem分配器
复制代码
  1. view plaincopy to clipboardprint?/*
  2. * node_bootmem_map is a map pointer - the bits represent all physical  
  3. * memory pages (including holes) on the node.
  4. */  
  5. /*用于bootmem分配器的节点数据结构*/  
  6. typedef struct bootmem_data {  
  7.     unsigned long node_min_pfn;/*存放bootmem位图的第一个页面(即内核映象结束处的第一个页面)。*/  
  8.     unsigned long node_low_pfn;/*物理内存的顶点,最高不超过896MB。*/  
  9.     void *node_bootmem_map;  
  10.     unsigned long last_end_off;/*用来存放在前一次分配中所分配的最后一个字节相对于last_pos的位移量*/  
  11.     unsigned long hint_idx;/*存放前一次分配的最后一个页面号*/  
  12.     struct list_head list;  
  13. } bootmem_data_t;  
  14. /*
  15. * node_bootmem_map is a map pointer - the bits represent all physical
  16. * memory pages (including holes) on the node.
  17. */
  18. /*用于bootmem分配器的节点数据结构*/
  19. typedef struct bootmem_data {
  20.         unsigned long node_min_pfn;/*存放bootmem位图的第一个页面(即内核映象结束处的第一个页面)。*/
  21.         unsigned long node_low_pfn;/*物理内存的顶点,最高不超过896MB。*/
  22.         void *node_bootmem_map;
  23.         unsigned long last_end_off;/*用来存放在前一次分配中所分配的最后一个字节相对于last_pos的位移量*/
  24.         unsigned long hint_idx;/*存放前一次分配的最后一个页面号*/
  25.         struct list_head list;
  26. } bootmem_data_t;
复制代码
全局链表
  1. view plaincopy to clipboardprint?static struct list_head bdata_list __initdata = LIST_HEAD_INIT(bdata_list);  
  2. static struct list_head bdata_list __initdata = LIST_HEAD_INIT(bdata_list);
复制代码
二、启动分配器的建立

启动分配器的建立主要的流程为初始化映射位图、活动内存区的映射位置0(表示可用)、保留内存区域处理,其中保留区存放在上面介绍的全局数组中,这里只是将分配器中对应映射位图值1,表示已经分配。

下面我们看内核中具体的初始化流程。

[code]start_kernel()->setup_arch()->initmem_init()


view plaincopy to clipboardprint?void __init setup_arch(char **cmdline_p)  
{  
          .......  
<span style="white-space: pre; "> </span>/*此函数在开始对bootmem分配制度建立做些准备工作  
    然后调用相关函数建立bootmem分配制度*/  
    initmem_init(0, max_pfn);  
          .......  
}            
void __init setup_arch(char **cmdline_p)
{
          .......
<span style="white-space: pre; ">        </span>/*此函数在开始对bootmem分配制度建立做些准备工作
        然后调用相关函数建立bootmem分配制度*/
        initmem_init(0, max_pfn);
          .......
}           
view plaincopy to clipboardprint?<span style="font-family: Arial, Verdana, sans-serif; "><span style="white-space: normal; "></span></span>  
<span style="font-family: Arial, Verdana, sans-serif; "><span style="white-space: normal; "></span></span>view plaincopy to clipboardprint?<span style="font-family: Arial, Verdana, sans-serif; "><span style="white-space: normal; "></span></span><pre name="code" class="cpp">void __init initmem_init(unsigned long start_pfn,  
                  unsigned long end_pfn)  
{  
#ifdef CONFIG_HIGHMEM  
    highstart_pfn = highend_pfn = max_pfn;  
    if (max_pfn > max_low_pfn)  
        highstart_pfn = max_low_pfn;  
    /*将活动内存放到early_node_map中,前面已经分析过了*/  
    e820_register_active_regions(0, 0, highend_pfn);  
    /*设置上面变量中的内存为当前,在这里没有  
    设置相关的宏*/  
    sparse_memory_present_with_active_regions(0);  
    printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",  
        pages_to_mb(highend_pfn - highstart_pfn));  
    num_physpages = highend_pfn;  
    /*高端内存开始地址物理*/  
    high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1;  
#else  
    e820_register_active_regions(0, 0, max_low_pfn);  
    sparse_memory_present_with_active_regions(0);  
    num_physpages = max_low_pfn;  
    high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1;  
#endif  
#ifdef CONFIG_FLATMEM  
    max_mapnr = num_physpages;  
#endif  
    __vmalloc_start_set = true;  
  
    printk(KERN_NOTICE "%ldMB LOWMEM available.\n",  
            pages_to_mb(max_low_pfn));  
    /*安装bootmem分配器,此分配器在伙伴系统起来之前  
    用来进行承担内存的分配等管理*/  
    setup_bootmem_allocator();  
}  
<span style="font-family: Arial, Verdana, sans-serif; "><span style="white-space: normal; "></span></span><pre name="code" class="cpp">void __init initmem_init(unsigned long start_pfn,
                                  unsigned long end_pfn)
{
#ifdef CONFIG_HIGHMEM
        highstart_pfn = highend_pfn = max_pfn;
        if (max_pfn > max_low_pfn)
                highstart_pfn = max_low_pfn;
        /*将活动内存放到early_node_map中,前面已经分析过了*/
        e820_register_active_regions(0, 0, highend_pfn);
        /*设置上面变量中的内存为当前,在这里没有
        设置相关的宏*/
        sparse_memory_present_with_active_regions(0);
        printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
                pages_to_mb(highend_pfn - highstart_pfn));
        num_physpages = highend_pfn;
        /*高端内存开始地址物理*/
        high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1;
#else
        e820_register_active_regions(0, 0, max_low_pfn);
        sparse_memory_present_with_active_regions(0);
        num_physpages = max_low_pfn;
        high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1;
#endif
#ifdef CONFIG_FLATMEM
        max_mapnr = num_physpages;
#endif
        __vmalloc_start_set = true;

        printk(KERN_NOTICE "%ldMB LOWMEM available.\n",
                        pages_to_mb(max_low_pfn));
        /*安装bootmem分配器,此分配器在伙伴系统起来之前
        用来进行承担内存的分配等管理*/
        setup_bootmem_allocator();
}



view plaincopy to clipboardprint?void __init setup_bootmem_allocator(void)  
{  
    int nodeid;  
    unsigned long bootmap_size, bootmap;  
    /*
     * Initialize the boot-time allocator (with low memory only):
     */  
     /*计算所需要的映射页面大小一个字节一位,
     所以需要对总的页面大小除以8*/  
    bootmap_size = bootmem_bootmap_pages(max_low_pfn)<<PAGE_SHIFT;  
    /*直接中e820中找到一个大小合适的内存块,返回基址*/  
    bootmap = find_e820_area(0, max_pfn_mapped<<PAGE_SHIFT, bootmap_size,  
                 PAGE_SIZE);  
    if (bootmap == -1L)  
        panic("Cannot find bootmem map of size %ld\n", bootmap_size);  
    /*将用于位图映射的页面保留*/  
    reserve_early(bootmap, bootmap + bootmap_size, "BOOTMAP");  
  
    printk(KERN_INFO "  mapped low ram: 0 - %08lx\n",  
         max_pfn_mapped<<PAGE_SHIFT);  
    printk(KERN_INFO "  low ram: 0 - %08lx\n", max_low_pfn<<PAGE_SHIFT);  
    /*对每一个在线的node*/  
    for_each_online_node(nodeid) {  
         unsigned long start_pfn, end_pfn;  
  
#ifdef CONFIG_NEED_MULTIPLE_NODES/*not set*/   
        start_pfn = node_start_pfn[nodeid];  
        end_pfn = node_end_pfn[nodeid];  
        if (start_pfn > max_low_pfn)  
            continue;  
        if (end_pfn > max_low_pfn)  
            end_pfn = max_low_pfn;  
#else   
        start_pfn = 0;  
        end_pfn = max_low_pfn;  
#endif   
        /*对指定节点安装启动分配器*/  
        bootmap = setup_node_bootmem(nodeid, start_pfn, end_pfn,  
                         bootmap);  
    }  
    /*bootmem的分配制度到这里就已经建立完成,把after_bootmem
    变量置成1,标识*/  
    after_bootmem = 1;  
}  
void __init setup_bootmem_allocator(void)
{
        int nodeid;
        unsigned long bootmap_size, bootmap;
        /*
         * Initialize the boot-time allocator (with low memory only):
         */
         /*计算所需要的映射页面大小一个字节一位,
         所以需要对总的页面大小除以8*/
        bootmap_size = bootmem_bootmap_pages(max_low_pfn)<<PAGE_SHIFT;
        /*直接中e820中找到一个大小合适的内存块,返回基址*/
        bootmap = find_e820_area(0, max_pfn_mapped<<PAGE_SHIFT, bootmap_size,
                                 PAGE_SIZE);
        if (bootmap == -1L)
                panic("Cannot find bootmem map of size %ld\n", bootmap_size);
        /*将用于位图映射的页面保留*/
        reserve_early(bootmap, bootmap + bootmap_size, "BOOTMAP");

        printk(KERN_INFO "  mapped low ram: 0 - %08lx\n",
                 max_pfn_mapped<<PAGE_SHIFT);
        printk(KERN_INFO "  low ram: 0 - %08lx\n", max_low_pfn<<PAGE_SHIFT);
        /*对每一个在线的node*/
        for_each_online_node(nodeid) {
                 unsigned long start_pfn, end_pfn;

#ifdef CONFIG_NEED_MULTIPLE_NODES/*not set*/
                start_pfn = node_start_pfn[nodeid];
                end_pfn = node_end_pfn[nodeid];
                if (start_pfn > max_low_pfn)
                        continue;
                if (end_pfn > max_low_pfn)
                        end_pfn = max_low_pfn;
#else
                start_pfn = 0;
                end_pfn = max_low_pfn;
#endif
                /*对指定节点安装启动分配器*/
                bootmap = setup_node_bootmem(nodeid, start_pfn, end_pfn,
                                                 bootmap);
        }
        /*bootmem的分配制度到这里就已经建立完成,把after_bootmem
        变量置成1,标识*/
        after_bootmem = 1;
}view plaincopy to clipboardprint?static unsigned long __init setup_node_bootmem(int nodeid,  
                 unsigned long start_pfn,  
                 unsigned long end_pfn,  
                 unsigned long bootmap)  
{  
    unsigned long bootmap_size;  
  
    /* don't touch min_low_pfn */  
    /*初始化映射位图,将位图中的所有位置1*/  
    bootmap_size = init_bootmem_node(NODE_DATA(nodeid),  
                     bootmap >> PAGE_SHIFT,  
                     start_pfn, end_pfn);  
    printk(KERN_INFO "  node %d low ram: %08lx - %08lx\n",  
        nodeid, start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);  
    printk(KERN_INFO "  node %d bootmap %08lx - %08lx\n",  
         nodeid, bootmap, bootmap + bootmap_size);  
    /*将活动内存区对应位图相关位置0,表示可被分配的*/  
    free_bootmem_with_active_regions(nodeid, end_pfn);  
    /*对置保留位的相关页面对应的位图设置为1,表示已经分配
    或者不可用(不能被分配)*/  
    early_res_to_bootmem(start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);  
    /*返回映射页面的最后地址,下次映射即可以从这里开始*/  
    return bootmap + bootmap_size;  
}  
static unsigned long __init setup_node_bootmem(int nodeid,
                                 unsigned long start_pfn,
                                 unsigned long end_pfn,
                                 unsigned long bootmap)
{
        unsigned long bootmap_size;

        /* don't touch min_low_pfn */
        /*初始化映射位图,将位图中的所有位置1*/
        bootmap_size = init_bootmem_node(NODE_DATA(nodeid),
                                         bootmap >> PAGE_SHIFT,
                                         start_pfn, end_pfn);
        printk(KERN_INFO "  node %d low ram: %08lx - %08lx\n",
                nodeid, start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);
        printk(KERN_INFO "  node %d bootmap %08lx - %08lx\n",
                 nodeid, bootmap, bootmap + bootmap_size);
        /*将活动内存区对应位图相关位置0,表示可被分配的*/
        free_bootmem_with_active_regions(nodeid, end_pfn);
        /*对置保留位的相关页面对应的位图设置为1,表示已经分配
        或者不可用(不能被分配)*/
        early_res_to_bootmem(start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);
        /*返回映射页面的最后地址,下次映射即可以从这里开始*/
        return bootmap + bootmap_size;
}
对于初始化映射位图,最终调用init_bootmem_core()


view plaincopy to clipboardprint?/*
* Called once to set up the allocator itself.
*/  
static unsigned long __init init_bootmem_core(bootmem_data_t *bdata,  
    unsigned long mapstart, unsigned long start, unsigned long end)  
{  
    unsigned long mapsize;  
  
    mminit_validate_memmodel_limits(&start, &end);  
    bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart));  
    bdata->node_min_pfn = start;  
    bdata->node_low_pfn = end;  
    /*添加bdata变量到链表中*/  
    link_bootmem(bdata);  
  
    /*
     * Initially all pages are reserved - setup_arch() has to
     * register free RAM areas explicitly.
     */  
     /*计算本bdata的mapsize,也就是内存页面大小的1/8*/  
    mapsize = bootmap_bytes(end - start);  
    /*将所有map置1*/  
    memset(bdata->node_bootmem_map, 0xff, mapsize);  
  
    bdebug("nid=%td start=%lx map=%lx end=%lx mapsize=%lx\n",  
        bdata - bootmem_node_data, start, mapstart, end, mapsize);  
  
    return mapsize;  
}  
/*
* Called once to set up the allocator itself.
*/
static unsigned long __init init_bootmem_core(bootmem_data_t *bdata,
        unsigned long mapstart, unsigned long start, unsigned long end)
{
        unsigned long mapsize;

        mminit_validate_memmodel_limits(&start, &end);
        bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart));
        bdata->node_min_pfn = start;
        bdata->node_low_pfn = end;
        /*添加bdata变量到链表中*/
        link_bootmem(bdata);

        /*
         * Initially all pages are reserved - setup_arch() has to
         * register free RAM areas explicitly.
         */
         /*计算本bdata的mapsize,也就是内存页面大小的1/8*/
        mapsize = bootmap_bytes(end - start);
        /*将所有map置1*/
        memset(bdata->node_bootmem_map, 0xff, mapsize);

        bdebug("nid=%td start=%lx map=%lx end=%lx mapsize=%lx\n",
                bdata - bootmem_node_data, start, mapstart, end, mapsize);

        return mapsize;
}
view plaincopy to clipboardprint?/*
* link bdata in order
*/  
/*添加到链表,由添加的代码可知
链表中的数据开始位置为递增的*/  

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

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP