目录
1.概述
2.内核数据结构
3.相关函数
3.1 获取memblock_region
3.2 寻找空闲内存区域
3.3 向memblock_type添加memblock_region
3.4 扩充memblock_type中region数组空间
3.5 移除memblock_type中相应内存区域
参考文献:
1.概述
上次介绍了linux内核bootmem分配器,随着硬件的发展以及复杂化,内存检测已经从简单地向BIOS询问扩展内存块的大小演变为处理复杂的表,块,库和群集。在x86架构上, 内核启动初期首先使用early_res机制接替BIOS e820的工作, 然后再交给架构独立的bootmem分配器, 最后转由伙伴分配系统进行页面管理。然后有人认为可以去掉bootmem以此简化该过程并提交patch,最终人们提议使用PowerPC, SuperH和SPARC架构上的LMB内存分配器作为系统启动初期的内存分配器。LMB内存分配器也就是本文介绍的memblock分配器。
2.内核数据结构
1 struct memblock_type { 2 unsigned long cnt; /* number of regions */ 3 unsigned long max; /* size of the allocated array */ 4 phys_addr_t total_size; /* size of all regions */ 5 struct memblock_region *regions; 6 char *name; 7 };
memblock数据结构是LMB内存分配器核心;
bottom_up:表示内存分配方向;
current_limit:表示memblock所管理页面最大物理地址;
memory:表示memblock所管理全部内存;
reserved:表示已经分配出去的内存;
phymem:表示进行物理地址映射的内存;
1 struct memblock_type { 2 unsigned long cnt; /* number of regions */ 3 unsigned long max; /* size of the allocated array */ 4 phys_addr_t total_size; /* size of all regions */ 5 struct memblock_region *regions; 6 char *name; 7 };
cnt:表示memblock_region数组中元素个数;
max:表示memblock_type所具有的memblock_region数组元素最大数目;
total_size:表示全部memblock_region数组保存页面数;
regions:指向memblock_region数组;
name:指向memblock_type名;
1 struct memblock_region { 2 phys_addr_t base; 3 phys_addr_t size; 4 unsigned long flags; 5 #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP 6 int nid; 7 #endif 8 };
base:表示memblock_region所具有内存起始物理地址;
size:表示memblock_region所具有内存大小;
flags:标志位;
nid:表示memblock_region所具有内存的节点号;
3.相关函数
3.1 获取memblock_region
1 void __init_memblock __next_mem_range(u64 *idx, int nid, ulong flags, 2 struct memblock_type *type_a, 3 struct memblock_type *type_b, 4 phys_addr_t *out_start, 5 phys_addr_t *out_end, int *out_nid) 6 { 7 int idx_a = *idx & 0xffffffff; 8 int idx_b = *idx >> 32; 9 10 if (WARN_ONCE(nid == MAX_NUMNODES, 11 "Usage of MAX_NUMNODES is deprecated. Use NUMA_NO_NODE instead\n")) 12 nid = NUMA_NO_NODE; 13 14 for (; idx_a < type_a->cnt; idx_a++) { 15 struct memblock_region *m = &type_a->regions[idx_a]; 16 17 phys_addr_t m_start = m->base; 18 phys_addr_t m_end = m->base + m->size; 19 int m_nid = memblock_get_region_node(m); 20 21 /* only memory regions are associated with nodes, check it */ 22 if (nid != NUMA_NO_NODE && nid != m_nid) 23 continue; 24 25 /* skip hotpluggable memory regions if needed */ 26 if (movable_node_is_enabled() && memblock_is_hotpluggable(m)) 27 continue; 28 29 /* if we want mirror memory skip non-mirror memory regions */ 30 if ((flags & MEMBLOCK_MIRROR) && !memblock_is_mirror(m)) 31 continue; 32 33 /* skip nomap memory unless we were asked for it explicitly */ 34 if (!(flags & MEMBLOCK_NOMAP) && memblock_is_nomap(m)) 35 continue; 36 37 if (!type_b) { 38 if (out_start) 39 *out_start = m_start; 40 if (out_end) 41 *out_end = m_end; 42 if (out_nid) 43 *out_nid = m_nid; 44 idx_a++; 45 *idx = (u32)idx_a | (u64)idx_b << 32; 46 return; 47 } 48 49 /* scan areas before each reservation */ 50 for (; idx_b < type_b->cnt + 1; idx_b++) { 51 struct memblock_region *r; 52 phys_addr_t r_start; 53 phys_addr_t r_end; 54 55 r = &type_b->regions[idx_b]; 56 r_start = idx_b ? r[-1].base + r[-1].size : 0; 57 r_end = idx_b < type_b->cnt ? 58 r->base : ULLONG_MAX; 59 60 /* 61 * if idx_b advanced past idx_a, 62 * break out to advance idx_a 63 */ 64 if (r_start >= m_end) 65 break; 66 /* if the two regions intersect, we‘re done */ 67 if (m_start < r_end) { 68 if (out_start) 69 *out_start = 70 max(m_start, r_start); 71 if (out_end) 72 *out_end = min(m_end, r_end); 73 if (out_nid) 74 *out_nid = m_nid; 75 /* 76 * The region which ends first is 77 * advanced for the next iteration. 78 */ 79 if (m_end <= r_end) 80 idx_a++; 81 else 82 idx_b++; 83 *idx = (u32)idx_a | (u64)idx_b << 32; 84 return; 85 } 86 } 87 } 88 89 /* signal end of iteration */ 90 *idx = ULLONG_MAX; 91 }
该函数idx为type_a和type_b中起始索引组合,nid为寻找内存区域所在节点号,flags为标志位,out_start、out_end和out_nid为输出符合调节的内存区域物理地址范围及所在节点号,type_a为寻找内存区域必须处于其拥有内存范围内,type_b为寻找目标内存不在其拥有内存范围内。函数首先获取type_a和type_b中region数组起始索引,然后循环遍历寻找属于type_a但不属于type_b的首个内存区域。
1 void __init_memblock __next_mem_range_rev(u64 *idx, int nid, ulong flags, 2 struct memblock_type *type_a, 3 struct memblock_type *type_b, 4 phys_addr_t *out_start, 5 phys_addr_t *out_end, int *out_nid) 6 { 7 int idx_a = *idx & 0xffffffff; 8 int idx_b = *idx >> 32; 9 10 if (WARN_ONCE(nid == MAX_NUMNODES, "Usage of MAX_NUMNODES is deprecated. Use NUMA_NO_NODE instead\n")) 11 nid = NUMA_NO_NODE; 12 13 if (*idx == (u64)ULLONG_MAX) { 14 idx_a = type_a->cnt - 1; 15 if (type_b != NULL) 16 idx_b = type_b->cnt; 17 else 18 idx_b = 0; 19 } 20 21 for (; idx_a >= 0; idx_a--) { 22 struct memblock_region *m = &type_a->regions[idx_a]; 23 24 phys_addr_t m_start = m->base; 25 phys_addr_t m_end = m->base + m->size; 26 int m_nid = memblock_get_region_node(m); 27 28 /* only memory regions are associated with nodes, check it */ 29 if (nid != NUMA_NO_NODE && nid != m_nid) 30 continue; 31 32 /* skip hotpluggable memory regions if needed */ 33 if (movable_node_is_enabled() && memblock_is_hotpluggable(m)) 34 continue; 35 36 /* if we want mirror memory skip non-mirror memory regions */ 37 if ((flags & MEMBLOCK_MIRROR) && !memblock_is_mirror(m)) 38 continue; 39 40 /* skip nomap memory unless we were asked for it explicitly */ 41 if (!(flags & MEMBLOCK_NOMAP) && memblock_is_nomap(m)) 42 continue; 43 44 if (!type_b) { 45 if (out_start) 46 *out_start = m_start; 47 if (out_end) 48 *out_end = m_end; 49 if (out_nid) 50 *out_nid = m_nid; 51 idx_a--; 52 *idx = (u32)idx_a | (u64)idx_b << 32; 53 return; 54 } 55 56 /* scan areas before each reservation */ 57 for (; idx_b >= 0; idx_b--) { 58 struct memblock_region *r; 59 phys_addr_t r_start; 60 phys_addr_t r_end; 61 62 r = &type_b->regions[idx_b]; 63 r_start = idx_b ? r[-1].base + r[-1].size : 0; 64 r_end = idx_b < type_b->cnt ? 65 r->base : ULLONG_MAX; 66 /* 67 * if idx_b advanced past idx_a, 68 * break out to advance idx_a 69 */ 70 71 if (r_end <= m_start) 72 break; 73 /* if the two regions intersect, we‘re done */ 74 if (m_end > r_start) { 75 if (out_start) 76 *out_start = max(m_start, r_start); 77 if (out_end) 78 *out_end = min(m_end, r_end); 79 if (out_nid) 80 *out_nid = m_nid; 81 if (m_start >= r_start) 82 idx_a--; 83 else 84 idx_b--; 85 *idx = (u32)idx_a | (u64)idx_b << 32; 86 return; 87 } 88 } 89 } 90 /* signal end of iteration */ 91 *idx = ULLONG_MAX; 92 }
该函数与__next_mem_range()过程大致一致,只不过遍历方向是从高物理地址向低物理地址遍历。
3.2 寻找空闲内存区域
1 phys_addr_t __init_memblock memblock_find_in_range_node(phys_addr_t size, 2 phys_addr_t align, phys_addr_t start, 3 phys_addr_t end, int nid, ulong flags) 4 { 5 phys_addr_t kernel_end, ret; 6 7 /* pump up @end */ 8 if (end == MEMBLOCK_ALLOC_ACCESSIBLE) 9 end = memblock.current_limit; 10 11 /* avoid allocating the first page */ 12 start = max_t(phys_addr_t, start, PAGE_SIZE); 13 end = max(start, end); 14 kernel_end = __pa_symbol(_end);
该函数中size表示寻找物理内存大小(字节),align为物理内存对齐,start表示所寻找内存区域最小物理地址,end为最大物理地址,nid为节点号,flags为标志位。检查end是否等于MEMBLOCK_ALLOC_ACCESSIBLE,相等则设置end为memblock.current_limit,重新计算所查询物理地址范围。
1 /* 2 * try bottom-up allocation only when bottom-up mode 3 * is set and @end is above the kernel image. 4 */ 5 if (memblock_bottom_up() && end > kernel_end) { 6 phys_addr_t bottom_up_start; 7 8 /* make sure we will allocate above the kernel */ 9 bottom_up_start = max(start, kernel_end); 10 11 /* ok, try bottom-up allocation first */ 12 ret = __memblock_find_range_bottom_up(bottom_up_start, end, 13 size, align, nid, flags); 14 if (ret) 15 return ret; 16 17 /* 18 * we always limit bottom-up allocation above the kernel, 19 * but top-down allocation doesn‘t have the limit, so 20 * retrying top-down allocation may succeed when bottom-up 21 * allocation failed. 22 * 23 * bottom-up allocation is expected to be fail very rarely, 24 * so we use WARN_ONCE() here to see the stack trace if 25 * fail happens. 26 */ 27 WARN_ONCE(1, "memblock: bottom-up allocation failed, memory hotunplug may be affected\n"); 28 }
检查memblock.bottom_up和end>kenel_end是否均为真,即内存查询是否为从低地址项高地址且end高与内核最大虚拟地址所对应的物理地址,重新获取查询范围并进行查询。
1 return __memblock_find_range_top_down(start, end, size, align, nid, 2 flags);
最后进行从高地址到低地址进行查询。
1 static phys_addr_t __init_memblock 2 __memblock_find_range_bottom_up(phys_addr_t start, phys_addr_t end, 3 phys_addr_t size, phys_addr_t align, int nid, 4 ulong flags) 5 { 6 phys_addr_t this_start, this_end, cand; 7 u64 i; 8 9 for_each_free_mem_range(i, nid, flags, &this_start, &this_end, NULL) { 10 this_start = clamp(this_start, start, end); 11 this_end = clamp(this_end, start, end); 12 13 cand = round_up(this_start, align); 14 if (cand < this_end && this_end - cand >= size) 15 return cand; 16 } 17 18 return 0; 19 }
该函数会通过for_each_free_mem_range()宏对memblock中进行遍历(由低物理地址到高物理地址),寻找属于memblock.memory但不属于memblock.reserved且符合添加的物理内存区域。
1 static phys_addr_t __init_memblock 2 __memblock_find_range_top_down(phys_addr_t start, phys_addr_t end, 3 phys_addr_t size, phys_addr_t align, int nid, 4 ulong flags) 5 { 6 phys_addr_t this_start, this_end, cand; 7 u64 i; 8 9 for_each_free_mem_range_reverse(i, nid, flags, &this_start, &this_end, 10 NULL) { 11 this_start = clamp(this_start, start, end); 12 this_end = clamp(this_end, start, end); 13 14 if (this_end < size) 15 continue; 16 17 cand = round_down(this_end - size, align); 18 if (cand >= this_start) 19 return cand; 20 } 21 22 return 0; 23 }
该函数与__memblock_find_range_bottom_up()工作过程类似,只是遍历方向为高地址到低地址。
3.3 向memblock_type添加memblock_region
1 int __init_memblock memblock_add_range(struct memblock_type *type, 2 phys_addr_t base, phys_addr_t size, 3 int nid, unsigned long flags) 4 { 5 bool insert = false; 6 phys_addr_t obase = base; 7 phys_addr_t end = base + memblock_cap_size(base, &size); 8 int idx, nr_new; 9 struct memblock_region *rgn; 10 11 if (!size) 12 return 0; 13 14 /* special case for empty array */ 15 if (type->regions[0].size == 0) { 16 WARN_ON(type->cnt != 1 || type->total_size); 17 type->regions[0].base = base; 18 type->regions[0].size = size; 19 type->regions[0].flags = flags; 20 memblock_set_region_node(&type->regions[0], nid); 21 type->total_size = size; 22 return 0; 23 }
该函数base为添加内存起始物理地址,size为添加的物理内存大小(字节数目),nid为添加内存所在节点号,flasg为添加region标志位。首先计算获取所添加物理内存所在物理地址范围,然后检查memblock_type中region数组是否为空。如果为空则初始化region数组首个元素并返回。
1 repeat: 2 /* 3 * The following is executed twice. Once with %false @insert and 4 * then with %true. The first counts the number of regions needed 5 * to accommodate the new area. The second actually inserts them. 6 */ 7 base = obase; 8 nr_new = 0; 9 10 for_each_memblock_type(idx, type, rgn) { 11 phys_addr_t rbase = rgn->base; 12 phys_addr_t rend = rbase + rgn->size; 13 14 if (rbase >= end) 15 break; 16 if (rend <= base) 17 continue; 18 /* 19 * @rgn overlaps. If it separates the lower part of new 20 * area, insert that portion. 21 */ 22 if (rbase > base) { 23 #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP 24 WARN_ON(nid != memblock_get_region_node(rgn)); 25 #endif 26 WARN_ON(flags != rgn->flags); 27 nr_new++; 28 if (insert) 29 memblock_insert_region(type, idx++, base, 30 rbase - base, nid, 31 flags); 32 } 33 /* area below @rend is dealt with, forget about it */ 34 base = min(rend, end); 35 }
遍历memblock_type所具有的region数组,获取数组中基地址处于base~end之间的元素,如果寻找到并且insert为真则将处于base~rbase之间的内存插入数组中,nr_new表示新插入的元素会增加1,继续遍历。
1 /* insert the remaining portion */ 2 if (base < end) { 3 nr_new++; 4 if (insert) 5 memblock_insert_region(type, idx, base, end - base, 6 nid, flags); 7 } 8 9 if (!nr_new) 10 return 0;
经过遍历后,检查内存区域是否完全插入,如果有部分区域并未插入则检查insert是否为真,为真则将剩余部分也插入region数组中,否则继续运行。
1 /* 2 * If this was the first round, resize array and repeat for actual 3 * insertions; otherwise, merge and return. 4 */ 5 if (!insert) { 6 while (type->cnt + nr_new > type->max) 7 if (memblock_double_array(type, obase, size) < 0) 8 return -ENOMEM; 9 insert = true; 10 goto repeat; 11 } else { 12 memblock_merge_regions(type); 13 return 0; 14 }
检查insert是否为真,若为真则将memblock_type中的region数组进行合并,否则检查nr_new+type->cnt是否超出type中region数组最大数,超出则对type中region数组进行扩充,设置insert为真继续重复以上操作。
1 static void __init_memblock memblock_insert_region(struct memblock_type *type, 2 int idx, phys_addr_t base, 3 phys_addr_t size, 4 int nid, unsigned long flags) 5 { 6 struct memblock_region *rgn = &type->regions[idx]; 7 8 BUG_ON(type->cnt >= type->max); 9 memmove(rgn + 1, rgn, (type->cnt - idx) * sizeof(*rgn)); 10 rgn->base = base; 11 rgn->size = size; 12 rgn->flags = flags; 13 memblock_set_region_node(rgn, nid); 14 type->cnt++; 15 type->total_size += size; 16 }
该函数idx表示插入region数组索引,base表示内存物理起始地址,size表示内存大小(字节数目)。首先获取idx所对应memblock_region指针,再将索引处于idx~type->cnt范围的region元素复制到idx+1~cnt+1所对应数组中,最后进行初始化并增加type相应计数。
1 static void __init_memblock memblock_merge_regions(struct memblock_type *type) 2 { 3 int i = 0; 4 5 /* cnt never goes below 1 */ 6 while (i < type->cnt - 1) { 7 struct memblock_region *this = &type->regions[i]; 8 struct memblock_region *next = &type->regions[i + 1]; 9 10 if (this->base + this->size != next->base || 11 memblock_get_region_node(this) != 12 memblock_get_region_node(next) || 13 this->flags != next->flags) { 14 BUG_ON(this->base + this->size > next->base); 15 i++; 16 continue; 17 } 18 19 this->size += next->size; 20 /* move forward from next + 1, index of which is i + 2 */ 21 memmove(next, next + 1, (type->cnt - (i + 2)) * sizeof(*next)); 22 type->cnt--; 23 } 24 }
该函数遍历type中region数组,将内存区域连续的region合并。
3.4 扩充memblock_type中region数组空间
1 static int __init_memblock memblock_double_array(struct memblock_type *type, 2 phys_addr_t new_area_start, 3 phys_addr_t new_area_size) 4 { 5 struct memblock_region *new_array, *old_array; 6 phys_addr_t old_alloc_size, new_alloc_size; 7 phys_addr_t old_size, new_size, addr; 8 int use_slab = slab_is_available(); 9 int *in_slab; 10 11 /* We don‘t allow resizing until we know about the reserved regions 12 * of memory that aren‘t suitable for allocation 13 */ 14 if (!memblock_can_resize) 15 return -1;
该函数中new_area_start表示向type中添加物理内存区域起始物理地址,new_area_start表示内存区域大小。收件检查是否使用slab缓存以及是否可以扩充type中region数组大小。
1 /* Calculate new doubled size */ 2 old_size = type->max * sizeof(struct memblock_region); 3 new_size = old_size << 1; 4 /* 5 * We need to allocated new one align to PAGE_SIZE, 6 * so we can free them completely later. 7 */ 8 old_alloc_size = PAGE_ALIGN(old_size); 9 new_alloc_size = PAGE_ALIGN(new_size); 10 11 /* Retrieve the slab flag */ 12 if (type == &memblock.memory) 13 in_slab = &memblock_memory_in_slab; 14 else 15 in_slab = &memblock_reserved_in_slab;
获取原region数组大小并将其乘以2作为将要分配region数组大小,按页面对齐并根据type类型获取相应slab缓存使用标志位。
1 if (use_slab) { 2 new_array = kmalloc(new_size, GFP_KERNEL); 3 addr = new_array ? __pa(new_array) : 0; 4 } else { 5 /* only exclude range when trying to double reserved.regions */ 6 if (type != &memblock.reserved) 7 new_area_start = new_area_size = 0; 8 9 addr = memblock_find_in_range(new_area_start + new_area_size, 10 memblock.current_limit, 11 new_alloc_size, PAGE_SIZE); 12 if (!addr && new_area_size) 13 addr = memblock_find_in_range(0, 14 min(new_area_start, memblock.current_limit), 15 new_alloc_size, PAGE_SIZE); 16 17 new_array = addr ? __va(addr) : NULL; 18 } 19 if (!addr) { 20 pr_err("memblock: Failed to double %s array from %ld to %ld entries !\n", 21 type->name, type->max, type->max * 2); 22 return -1; 23 }
开始分配新region数组内存空间,如果使用slab,则从slab缓存中分配,否则从memblock中分配。
1 memcpy(new_array, type->regions, old_size); 2 memset(new_array + type->max, 0, old_size); 3 old_array = type->regions; 4 type->regions = new_array; 5 type->max <<= 1; 6 7 /* Free old array. We needn‘t free it if the array is the static one */ 8 if (*in_slab) 9 kfree(old_array); 10 else if (old_array != memblock_memory_init_regions && 11 old_array != memblock_reserved_init_regions) 12 memblock_free(__pa(old_array), old_alloc_size); 13 14 /* 15 * Reserve the new array if that comes from the memblock. Otherwise, we 16 * needn‘t do it 17 */ 18 if (!use_slab) 19 BUG_ON(memblock_reserve(addr, new_alloc_size)); 20 21 /* Update slab flag */ 22 *in_slab = use_slab; 23 24 return 0;
最后复制原来region数组并将剩余空间设为0并释放原数组所在内存空间。
3.5 移除memblock_type中相应内存区域
1 static int __init_memblock memblock_isolate_range(struct memblock_type *type, 2 phys_addr_t base, phys_addr_t size, 3 int *start_rgn, int *end_rgn) 4 { 5 phys_addr_t end = base + memblock_cap_size(base, &size); 6 int idx; 7 struct memblock_region *rgn; 8 9 *start_rgn = *end_rgn = 0; 10 11 if (!size) 12 return 0; 13 14 15 /* we‘ll create at most two more regions */ 16 while (type->cnt + 2 > type->max) 17 if (memblock_double_array(type, base, size) < 0) 18 return -ENOMEM;
该函数从type中region数组分离指定范围物理内存,base表示分离内存区域起始物理地址,size为分离物理内存大小,start_rgn为输出分离后的内存区域起始region,end_rgn为结束region。如果type中region数组空间不足则扩充region数组。
1 for_each_memblock_type(idx, type, rgn) { 2 phys_addr_t rbase = rgn->base; 3 phys_addr_t rend = rbase + rgn->size; 4 5 if (rbase >= end) 6 break; 7 if (rend <= base) 8 continue; 9 10 if (rbase < base) { 11 /* 12 * @rgn intersects from below. Split and continue 13 * to process the next region - the new top half. 14 */ 15 rgn->base = base; 16 rgn->size -= base - rbase; 17 type->total_size -= base - rbase; 18 memblock_insert_region(type, idx, rbase, base - rbase, 19 memblock_get_region_node(rgn), 20 rgn->flags); 21 } else if (rend > end) { 22 /* 23 * @rgn intersects from above. Split and redo the 24 * current region - the new bottom half. 25 */ 26 rgn->base = end; 27 rgn->size -= end - rbase; 28 type->total_size -= end - rbase; 29 memblock_insert_region(type, idx--, rbase, end - rbase, 30 memblock_get_region_node(rgn), 31 rgn->flags); 32 } else { 33 /* @rgn is fully contained, record it */ 34 if (!*end_rgn) 35 *start_rgn = idx; 36 *end_rgn = idx + 1; 37 } 38 } 39 40 return 0;
循环遍历type中region数组,对于处于分离内存区域的region进行分割并重新插入到数组中,最后返回索引。
1 static void __init_memblock memblock_remove_region(struct memblock_type *type, unsigned long r) 2 { 3 type->total_size -= type->regions[r].size; 4 memmove(&type->regions[r], &type->regions[r + 1], 5 (type->cnt - (r + 1)) * sizeof(type->regions[r])); 6 type->cnt--; 7 8 /* Special case for empty arrays */ 9 if (type->cnt == 0) { 10 WARN_ON(type->total_size != 0); 11 type->cnt = 1; 12 type->regions[0].base = 0; 13 type->regions[0].size = 0; 14 type->regions[0].flags = 0; 15 memblock_set_region_node(&type->regions[0], MAX_NUMNODES); 16 } 17 }
将type中指定索引region移除数组。
1 static int __init_memblock memblock_remove_range(struct memblock_type *type, 2 phys_addr_t base, phys_addr_t size) 3 { 4 int start_rgn, end_rgn; 5 int i, ret; 6 7 ret = memblock_isolate_range(type, base, size, &start_rgn, &end_rgn); 8 if (ret) 9 return ret; 10 11 for (i = end_rgn - 1; i >= start_rgn; i--) 12 memblock_remove_region(type, i); 13 return 0; 14 }
从type移除处于给定范围内物理内存区域。
注:以上均为自己对linux内核4.15.1源码的分析。如果有不足之处,欢迎大家指出。
参考文献:
[1]内存分配器memblock.