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

linux启动内存分配器

linux启动内存分配器






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

一、数据结构介绍

1,保留区间

因为在建立启动内存分配器的时候,会涉及保留内存。也就是说,之前保留给页表、分配器本身(用于映射的位图)、io等得内存在分配器建立后,当用它来分配内存空间时,保留出来的那些部分就不能再分配了。linux中对保留内存空间的部分用下列数据结构表示view plaincopy to clipboardprint?/*
* Early reserved memory areas.
*/
#define MAX_EARLY_RES 20/*保留空间最大块数*/   

struct early_res {/*保留空间结构*/
    u64 start, end;
    char name;
    char overlap_ok;
};
/*保留内存空间全局变量*/
static struct early_res early_res __initdata = {
    { 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */
    {}
};
/*
* Early reserved memory areas.
*/
#define MAX_EARLY_RES 20/*保留空间最大块数*/

struct early_res {/*保留空间结构*/
        u64 start, end;
        char name;
        char overlap_ok;
};
/*保留内存空间全局变量*/
static struct early_res early_res __initdata = {
        { 0, PAGE_SIZE, "BIOS data page" },        /* BIOS data page */
        {}
};2,bootmem分配器


view plaincopy to clipboardprint?/*
* node_bootmem_map is a map pointer - the bits represent all physical
* memory pages (including holes) on the node.
*/
/*用于bootmem分配器的节点数据结构*/
typedef struct bootmem_data {
    unsigned long node_min_pfn;/*存放bootmem位图的第一个页面(即内核映象结束处的第一个页面)。*/
    unsigned long node_low_pfn;/*物理内存的顶点,最高不超过896MB。*/
    void *node_bootmem_map;
    unsigned long last_end_off;/*用来存放在前一次分配中所分配的最后一个字节相对于last_pos的位移量*/
    unsigned long hint_idx;/*存放前一次分配的最后一个页面号*/
    struct list_head list;
} bootmem_data_t;
/*
* node_bootmem_map is a map pointer - the bits represent all physical
* memory pages (including holes) on the node.
*/
/*用于bootmem分配器的节点数据结构*/
typedef struct bootmem_data {
        unsigned long node_min_pfn;/*存放bootmem位图的第一个页面(即内核映象结束处的第一个页面)。*/
        unsigned long node_low_pfn;/*物理内存的顶点,最高不超过896MB。*/
        void *node_bootmem_map;
        unsigned long last_end_off;/*用来存放在前一次分配中所分配的最后一个字节相对于last_pos的位移量*/
        unsigned long hint_idx;/*存放前一次分配的最后一个页面号*/
        struct list_head list;
} bootmem_data_t;全局链表view plaincopy to clipboardprint?static struct list_head bdata_list __initdata = LIST_HEAD_INIT(bdata_list);
static struct list_head bdata_list __initdata = LIST_HEAD_INIT(bdata_list); 二、启动分配器的建立

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

下面我们看内核中具体的初始化流程。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();
}

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

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