linux中bootmem分析

转载地址:https://blog.youkuaiyun.com/u010383937/article/details/78596635

bootmem机制是在linux启动期间,buddy管理器,slab管理器没有初始化好时使用的内存管理方法。

之后系统起来之后,就交由buddy等方法来管理了。


在多核系统中,每个核都对应这个一个node_data结构体,其中就记录着这个核使用的内存,也包括了bootmem的信息。

看一下node_data的结构体


 
 
  1. #define LEVELS_PER_SLICE 128
  2. struct slice_data {
  3. unsigned long irq_enable_mask[ 2];
  4. int level_to_irq[LEVELS_PER_SLICE];
  5. };
  6. struct hub_data {
  7. cpumask_t h_cpus;
  8. unsigned long slice_map;
  9. unsigned long irq_alloc_mask[ 2];
  10. struct slice_data slice[2];
  11. };
  12. struct node_data {
  13. struct pglist_data pglist; //记录内存信息
  14. struct hub_data hub;
  15. cpumask_t cpumask;
  16. };
  17. #define NODE_DATA(n) (&__node_data[(n)]->pglist)
  18. #define hub_data(n) (&__node_data[(n)]->hub)


 
 
  1. struct bootmem_data;
  2. typedef struct pglist_data {
  3. struct zone node_zones[MAX_NR_ZONES]; //zone信息
  4. struct zonelist node_zonelists[MAX_ZONELISTS]; //zone链表
  5. int nr_zones;
  6. #ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */
  7. struct page *node_mem_map;
  8. #ifdef CONFIG_MEMCG
  9. struct page_cgroup *node_page_cgroup;
  10. #endif
  11. #endif
  12. #ifndef CONFIG_NO_BOOTMEM
  13. struct bootmem_data *bdata; //bootmem信息
  14. #endif
  15. #ifdef CONFIG_MEMORY_HOTPLUG
  16. spinlock_t node_size_lock;
  17. #endif
  18. unsigned long node_start_pfn; //物理地址开始
  19. unsigned long node_present_pages; /*总的物理页数*/
  20. unsigned long node_spanned_pages; /*总的物理大小,包括hole*/
  21. int node_id; //node号
  22. nodemask_t reclaim_nodes; /* Nodes allowed to reclaim from */
  23. wait_queue_head_t kswapd_wait;
  24. wait_queue_head_t pfmemalloc_wait;
  25. struct task_struct *kswapd; /* Protected by lock_memory_hotplug() */
  26. int kswapd_max_order;
  27. enum zone_type classzone_idx;
  28. #ifdef CONFIG_NUMA_BALANCING
  29. /* Lock serializing the migrate rate limiting window */
  30. spinlock_t numabalancing_migrate_lock;
  31. /* Rate limiting time interval */
  32. unsigned long numabalancing_migrate_next_window;
  33. /*Number of pages migrated during the rate limiting time interval */
  34. unsigned long numabalancing_migrate_nr_pages;
  35. #endif
  36. } pg_data_t;


 
 
  1. typedef struct bootmem_data {
  2. unsigned long node_min_pfn; //地址开始
  3. unsigned long node_low_pfn; //地址结束
  4. void *node_bootmem_map; // 位图信息存储位置
  5. unsigned long last_end_off; //分配的最后一个页内的偏移
  6. unsigned long hint_idx; //
  7. struct list_head list; //用于链表
  8. } bootmem_data_t;


 
 
  1. //每个cpu都对应一个bootmem
  2. bootmem_data_t bootmem_node_data[MAX_NUMNODES] __initdata;
  3. //为每个cpu定义的node_data
  4. struct node_data *__node_data[MAX_NUMNODES];
  5. EXPORT_SYMBOL(__node_data);

bootmem的操作方法:


 
 
  1. bootmem的操作函数:
  2. #define alloc_bootmem(x) \
  3. __alloc_bootmem(x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)
  4. #define alloc_bootmem_align(x, align) \
  5. __alloc_bootmem(x, align, BOOTMEM_LOW_LIMIT)
  6. #define alloc_bootmem_nopanic(x) \
  7. __alloc_bootmem_nopanic(x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)
  8. #define alloc_bootmem_pages(x) \
  9. __alloc_bootmem(x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)
  10. #define alloc_bootmem_pages_nopanic(x) \
  11. __alloc_bootmem_nopanic(x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)
  12. #define alloc_bootmem_node(pgdat, x) \
  13. __alloc_bootmem_node(pgdat, x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)
  14. #define alloc_bootmem_node_nopanic(pgdat, x) \
  15. __alloc_bootmem_node_nopanic(pgdat, x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)
  16. #define alloc_bootmem_pages_node(pgdat, x) \
  17. __alloc_bootmem_node(pgdat, x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)
  18. #define alloc_bootmem_pages_node_nopanic(pgdat, x) \
  19. __alloc_bootmem_node_nopanic(pgdat, x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)
  20. #define alloc_bootmem_low(x) \
  21. __alloc_bootmem_low(x, SMP_CACHE_BYTES, 0)
  22. #define alloc_bootmem_low_pages_nopanic(x) \
  23. __alloc_bootmem_low_nopanic(x, PAGE_SIZE, 0)
  24. #define alloc_bootmem_low_pages(x) \
  25. __alloc_bootmem_low(x, PAGE_SIZE, 0)
  26. #define alloc_bootmem_low_pages_node(pgdat, x) \
  27. __alloc_bootmem_low_node(pgdat, x, PAGE_SIZE, 0)

讲解一个bootmem内存分配方法:

 
 
  1. /**参数的作用
  2. * __alloc_bootmem - allocate boot memory
  3. * @size : size of the request in bytes
  4. * @align : alignment of the region
  5. * @goal : preferred starting address of the region
  6. *
  7. * The goal is dropped if it can not be satisfied and the allocation will
  8. * fall back to memory below @goal .
  9. *
  10. * Allocation may happen on any node in the system.
  11. *
  12. * The function panics if the request can not be satisfied.
  13. */
  14. void * __init __alloc_bootmem(unsigned long size, unsigned long align,
  15. unsigned long goal)
  16. {
  17. unsigned long limit = 0;
  18. return ___alloc_bootmem(size, align, goal, limit);
  19. }

 
 
  1. static void * __init ___alloc_bootmem( unsigned long size, unsigned long align, unsigned long goal, unsigned long limit)
  2. {
  3. void *mem = ___alloc_bootmem_nopanic(size, align, goal, limit);
  4. if (mem)
  5. return mem;
  6. printk(KERN_ALERT "bootmem alloc of %lu bytes failed!\n", size);
  7. panic( "Out of memory");
  8. return NULL;
  9. }


 
 
  1. static void * __init ___alloc_bootmem_nopanic( unsigned long size,
  2. unsigned long align,
  3. unsigned long goal,
  4. unsigned long limit)
  5. {
  6. void *ptr;
  7. restart:
  8. ptr = alloc_bootmem_core(size, align, goal, limit);
  9. if (ptr)
  10. return ptr;
  11. if (goal) {
  12. goal = 0;
  13. goto restart;
  14. }
  15. return NULL;
  16. }


 
 
  1. static void * __ init alloc_bootmem_core(unsigned long size,
  2. unsigned long align,
  3. unsigned long goal,
  4. unsigned long limit)
  5. {
  6. bootmem_data_t *bdata;
  7. void *region;
  8. //循环查找bdata_list,在bootmem中查找一个合适的内存区域
  9. list_for_each_entry(bdata, &bdata_list, list) {
  10. if (goal && bdata->node_low_pfn <= PFN_DOWN(goal))
  11. continue;
  12. if (limit && bdata->node_min_pfn >= PFN_DOWN(limit))
  13. break;
  14. //然后调用alloc_bootmem_bdata进行分配
  15. region = alloc_bootmem_bdata(bdata, size, align, goal, limit);
  16. if (region)
  17. return region;
  18. }
  19. return NULL;
  20. }



 
 
  1. static void * __ init alloc_bootmem_bdata(struct bootmem_data *bdata,unsigned long size, unsigned long align,unsigned long goal, unsigned long limit)
  2. {
  3. unsigned long fallback = 0;
  4. unsigned long min, max, start, sidx, midx, step;
  5. //如果size为0,报错
  6. BUG_ON(!size);
  7. BUG_ON(align & (align - 1));
  8. //limit存在并且goal+size>limit报错
  9. BUG_ON(limit && goal + size > limit);
  10. //如果bdata的bootmem为空,则报错
  11. if (!bdata->node_bootmem_map)
  12. return NULL;
  13. //获取最小,最大地址
  14. min = bdata->node_min_pfn;
  15. max = bdata->node_low_pfn;
  16. //得到goal和limit的物理地址
  17. goal >>= PAGE_SHIFT;
  18. limit >>= PAGE_SHIFT;
  19. if (limit && max > limit)
  20. max = limit;
  21. if (max <= min)
  22. return NULL;
  23. //设置步长,也就是在位图操作时,每次跳过几位
  24. step = max(align >> PAGE_SHIFT, 1UL);
  25. //goal为指定的查找区域的开始地址,在这里把goal的对齐格式更新后,赋值给start
  26. if (goal && min < goal && goal < max)
  27. start = ALIGN(goal, step);
  28. else
  29. start = ALIGN(min, step);
  30. //得到查找区域相对于bdata开始位置的偏移,以及获取最大偏移
  31. sidx = start - bdata->node_min_pfn;
  32. midx = max - bdata->node_min_pfn;
  33. //hint为上次分配的偏移
  34. if (bdata->hint_idx > sidx) {
  35. /*
  36. * Handle the valid case of sidx being zero and still
  37. * catch the fallback below.
  38. */
  39. fallback = sidx + 1;
  40. sidx = align_idx(bdata, bdata->hint_idx, step);
  41. }
  42. while ( 1) {
  43. int merge;
  44. void *region;
  45. unsigned long eidx, i, start_off, end_off;
  46. find_block:
  47. //在位图中(node_bootmem_map)中查找下一个为零的地方,为零代表可用
  48. sidx = find_next_zero_bit(bdata->node_bootmem_map, midx, sidx);
  49. sidx = align_idx(bdata, sidx, step);
  50. eidx = sidx + PFN_UP(size);
  51. if (sidx >= midx || eidx > midx)
  52. break;
  53. for (i = sidx; i < eidx; i++)
  54. if (test_bit(i, bdata->node_bootmem_map)) {
  55. sidx = align_idx(bdata, i, step);
  56. if (sidx == i)
  57. sidx += step;
  58. goto find_block;
  59. }
  60. //这里的start_off即为分配的物理地址开始
  61. if (bdata->last_end_off & (PAGE_SIZE - 1) &&
  62. PFN_DOWN(bdata->last_end_off) + 1 == sidx)
  63. start_off = align_off(bdata, bdata->last_end_off, align);
  64. else
  65. start_off = PFN_PHYS(sidx);
  66. //这里的merge是什么作用????????????????
  67. merge = PFN_DOWN(start_off) < sidx;
  68. //end_off分配的物理地址的结束地址
  69. end_off = start_off + size;
  70. //把end_off赋值给last_end_off;last_and_off表示上次分配的页内偏移,分配完,其为0
  71. bdata->last_end_off = end_off;
  72. //hint表示上次分配的页号
  73. bdata->hint_idx = PFN_UP(end_off);
  74. //把分配的地址设置为已配置
  75. if (__reserve(bdata, PFN_DOWN(start_off) + merge,
  76. PFN_UP(end_off), BOOTMEM_EXCLUSIVE))
  77. BUG();
  78. //region表示分配地址的开始,清零后返回
  79. region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
  80. start_off);
  81. memset(region, 0, size);
  82. kmemleak_alloc(region, size, 0, 0);
  83. return region;
  84. }
  85. if (fallback) {
  86. sidx = align_idx(bdata, fallback - 1, step);
  87. fallback = 0;
  88. goto find_block;
  89. }
  90. return NULL;
  91. }




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值