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伙伴系统)

Linux内存从0到1学习笔记(6.7,物理内存初始化之CMA初始化)

Linux内存从0到1学习笔记(6.10 物理内存初始化之vmalloc分配器)