Linux内存从0到1学习笔记(六,物理内存初始化之四 --- 内存分配器)---持续更新
Posted 高桐@BILL
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内存从0到1学习笔记(六,物理内存初始化之四 --- 内存分配器)---持续更新相关的知识,希望对你有一定的参考价值。
写在前面
在linux启动的那一刻,内存管理就已经开始了。在内核中,实现物理内存管理的allocator包括:
-
初始化阶段物理内存管理memblock
-
连续物理内存管理buddy allocator
-
非连续物理内存管理vmalloc allocator
-
小块物理内存管理slab allocator
在系统初始化阶段会先启用一个bootmem分配器和memblock分配器,bootmem分配器管理着一个node结点的所有内存,包括numa架构中多个node有多个bootmem,它们被链入bdata_list链表中保存。而伙伴系统的初始化就是将bootmem管理的所有物理页框释放到伙伴系统中去。
接下来我们来简单梳理下,内存分配器的初始化流程(红色部分);
一、初始化buddy系统
系统内存中的每个物理页(页帧),都对应一个struct page实例。每个内存域都关联了一个struct zone的实例。其中保存了用于管理伙伴数据的主要数组。
mmzone.h
struct zone
...
//不通长度的空闲区域
struct free_area free_area[MAX_ORDER];
...
;
free_area是一个辅助数据结构。定义如下:
<mmzone.h>
struct free_area
struct list_head_head free_list[MIGRATE_TYPES];
unsigned long nr_free;
;
nr_free指定了当前内存区中空闲页块的数目。
free_list是用于链接空闲页的链表。
阶是伙伴系统中一个非常重要的术语。描述了内存分配的数量单位。内存块的长度是2的order次方。其中order的范围是从0到MAX_ORDER.
26 /* Free memory management - zoned buddy allocator. */ 27 #ifndef CONFIG_FORCE_MAX_ZONEORDER 28 #define MAX_ORDER 11 29 #else 30 #define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER 31 #endif 32 #define MAX_ORDER_NR_PAGES (1 << (MAX_ORDER - 1))
MAX_ORDER设置是11。
1.1 mem_init()
linux_mainline-5.17.0/arch/arm64/mm/init.c
374 void __init mem_init(void)
375
376 if (swiotlb_force == SWIOTLB_FORCE ||
377 max_pfn > PFN_DOWN(arm64_dma_phys_limit))
378 swiotlb_init(1);
379 else if (!xen_swiotlb_detect())
380 swiotlb_force = SWIOTLB_NO_FORCE;
381
382 /* this will put all unused low memory onto the freelists */
//伙伴系统便是使用页为单位对内存进行管理的方法。伙伴系统接管前,处理建立mem_section结构,也必须先从Mem Block中释放出不再使用的内存交给伙伴系统管理
383 memblock_free_all();
384
385 /*
386 * Check boundaries twice: Some fundamental inconsistencies can be
387 * detected at build time already.
388 */
389 #ifdef CONFIG_COMPAT
390 BUILD_BUG_ON(TASK_SIZE_32 > DEFAULT_MAP_WINDOW_64);
391 #endif
392
393 /*
394 * Selected page table levels should match when derived from
395 * scratch using the virtual address range and page size.
396 */
397 BUILD_BUG_ON(ARM64_HW_PGTABLE_LEVELS(CONFIG_ARM64_VA_BITS) !=
398 CONFIG_PGTABLE_LEVELS);
399
400 if (PAGE_SIZE >= 16384 && get_num_physpages() <= 128)
401 extern int sysctl_overcommit_memory;
402 /*
403 * On a machine this small we won't get anywhere without
404 * overcommit, so turn it on by default.
405 */
406 sysctl_overcommit_memory = OVERCOMMIT_ALWAYS;
407
408
1.2 memblock_free_all()
释放空闲页给到buddy分配器
linux_mainline-5.17.0/mm/memblock.c
2101 /**
2102 * memblock_free_all - release free pages to the buddy allocator
2103 */
2104 void __init memblock_free_all(void)
2105
2106 unsigned long pages;
2107
2108 free_unused_memmap();//释放未使用mem_map内存
2109 reset_all_zones_managed_pages();//将所有结点所有区域的managed_pages自动设置为0(managed_pages表示被伙伴系统管理的页的数量)。
2110
2111 pages = free_low_memory_core_early();//将reserve类型的memblock和明确标记为Memory None的内存对应的页做标记为reserved(PG_reserved).将Mem block类型为memory的区域free掉,并标记为Free页面
2112 totalram_pages_add(pages);//增加 _totalram_pages ,用于标记系统中可用总页数.
2113
1.3 buddy 分配页
alloc_page(mask,order),分配一个2的order次方并返回一个struct page的实例。
get_zeroed_page(mask),分配一页并返回一个page实例。
__get_free_page(mask,order)和__get_free_page(mask),返回对应分配内存块的虚拟地址。
get_dma_pages(gfp_mask,order),返回获得的DMA页。
1.4 buddy 释放页
free_page(struct page*)和free_pages(struct page *, order)用于将一个或2的order次方个页返回给内存管理子系统。
__free_page(struct page*)和__free_pages(struct page *, order)意义同上,但表示需要释放的内存区时,使用了虚拟地址而非page实例。
二、初始化Slab分配器
kmem_cache_init()主要完成内核slub内存分配体系的初始化。
huang:
linux_mainline-5.17.0/mm/slab.c
//slab(slub)初始化
1199 /*
1200 * Initialisation. Called after the page allocator have been initialised and
1201 * before smp_init().
1202 */
1203 void __init kmem_cache_init(void)
1204
1205 int i;
1206 //kmem_cache_init创建系统中的第一个slab缓存,以便为kmem_cache的实例提供内存。为此,内核使用的 主要是在编译时创建的静态数据。实际上,一个静态数据结构(initarray_cache)用作per-CPU数组。 该缓存的名称是kmem_cache_boot。
1207 kmem_cache = &kmem_cache_boot;
1208
1209 if (!IS_ENABLED(CONFIG_NUMA) || num_possible_nodes() == 1)
1210 use_alien_caches = 0;
1211
1212 for (i = 0; i < NUM_INIT_LISTS; i++)
1213 kmem_cache_node_init(&init_kmem_cache_node[i]);
1214
1215 /*
1216 * Fragmentation resistance on low memory - only use bigger
1217 * page orders on machines with more than 32MB of memory if
1218 * not overridden on the command line.
1219 */
1220 if (!slab_max_order_set && totalram_pages() > (32 << 20) >> PAGE_SHIFT)
1221 slab_max_order = SLAB_MAX_ORDER_HI;
1222
1223 /* Bootstrap is tricky, because several objects are allocated
1224 * from caches that do not exist yet:
1225 * 1) initialize the kmem_cache cache: it contains the struct
1226 * kmem_cache structures of all caches, except kmem_cache itself:
1227 * kmem_cache is statically allocated.
1228 * Initially an __init data area is used for the head array and the
1229 * kmem_cache_node structures, it's replaced with a kmalloc allocated
1230 * array at the end of the bootstrap.
1231 * 2) Create the first kmalloc cache.
1232 * The struct kmem_cache for the new cache is allocated normally.
1233 * An __init data area is used for the head array.
1234 * 3) Create the remaining kmalloc caches, with minimally sized
1235 * head arrays.
1236 * 4) Replace the __init data head arrays for kmem_cache and the first
1237 * kmalloc cache with kmalloc allocated arrays.
1238 * 5) Replace the __init data for kmem_cache_node for kmem_cache and
1239 * the other cache's with kmalloc allocated memory.
1240 * 6) Resize the head arrays of the kmalloc caches to their final sizes.
1241 */
1242
1243 /* 1) create the kmem_cache */
1244
1245 /*
1246 * struct kmem_cache size depends on nr_node_ids & nr_cpu_ids
1247 */
//调用create_boot_cache创建一个名为kmem_cache的缓存.并添加到slab_caches链中.
1248 create_boot_cache(kmem_cache, "kmem_cache",
1249 offsetof(struct kmem_cache, node) +
1250 nr_node_ids * sizeof(struct kmem_cache_node *),
1251 SLAB_HWCACHE_ALIGN, 0, 0);
1252 list_add(&kmem_cache->list, &slab_caches);
1253 slab_state = PARTIAL;
1254
1255 /*
1256 * Initialize the caches that provide memory for the kmem_cache_node
1257 * structures first. Without this, further allocations will bug.
1258 */
1259 kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE] = create_kmalloc_cache(
1260 kmalloc_info[INDEX_NODE].name[KMALLOC_NORMAL],
1261 kmalloc_info[INDEX_NODE].size,
1262 ARCH_KMALLOC_FLAGS, 0,
1263 kmalloc_info[INDEX_NODE].size);
1264 slab_state = PARTIAL_NODE;
1265 setup_kmalloc_cache_index_table();
1266
1267 slab_early_init = 0;
1268
1269 /* 5) Replace the bootstrap kmem_cache_node */
1270
1271 int nid;
1272
1273 for_each_online_node(nid)
1274 init_list(kmem_cache, &init_kmem_cache_node[CACHE_CACHE + nid], nid);
1275
1276 init_list(kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE],
1277 &init_kmem_cache_node[SIZE_NODE + nid], nid);
1278
1279
1280
1281 create_kmalloc_caches(ARCH_KMALLOC_FLAGS);
1282
以上是关于Linux内存从0到1学习笔记(六,物理内存初始化之四 --- 内存分配器)---持续更新的主要内容,如果未能解决你的问题,请参考以下文章
Linux内存从0到1学习笔记(六,物理内存初始化之三 --- 物理内存管理数据结构)
Linux内存从0到1学习笔记(六,物理内存初始化之二 --- 内存模型)
Linux内存从0到1学习笔记(6.4,物理内存初始化之预留内存)
Linux内存从0到1学习笔记(6.8,物理内存初始化之buddy伙伴系统)