歡迎您光臨本站 註冊首頁

linux啟動內存分配器

←手機掃碼閱讀     火星人 @ 2014-03-03 , reply:0

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)  
{  
          .......  
/*此函數在開始對bootmem分配製度建立做些準備工作  
    然後調用相關函數建立bootmem分配製度*/  
    initmem_init(0, max_pfn);  
          .......  
}            
void __init setup_arch(char **cmdline_p)
{
          .......
        /*此函數在開始對bootmem分配製度建立做些準備工作
        然後調用相關函數建立bootmem分配製度*/
        initmem_init(0, max_pfn);
          .......
}           
view plaincopy to clipboardprint?  
view plaincopy to clipboardprint?
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();  
  }  
  
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;  
          end_pfn = node_end_pfn;  
          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;
                  end_pfn = node_end_pfn;
                  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 
   */  
   /*添加到鏈表,由添加的代碼可知 
   鏈表中的數據開始位置為遞增的*/  
  				
《解決方案》

謝謝分享

[火星人 ] linux啟動內存分配器已經有972次圍觀

http://coctec.com/docs/service/show-post-1206.html