内核源码解读之内存管理(10)percpu_page_set分析

Posted 奇妙之二进制

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内核源码解读之内存管理(10)percpu_page_set分析相关的知识,希望对你有一定的参考价值。

文章目录

背景

Linux系统中0阶内存分配需求是最多的, 而且经常存在频繁分配释放的行为,如果每次都去伙伴系统中申请,会经常需要获取zone->lock锁住整个zone区域。随着CPU核心数的增加,伙伴系统锁竞争激烈程度也会越来越大。
为了改善这个问题,linux内核中引入了per_cpu_pageset(下面简称pcp)。优化思路是先从zone一次性拿一些页出来,放到每个cpu自己本地中。释放也先放回到这里,等满了再一起还给zone。

数据结构

struct zone 
...
	struct per_cpu_pages	__percpu *per_cpu_pageset;
...
;

内核使用per_cpu_pageset数据结构记录pcp相关信息,其内嵌在zone数据结构中。

/* Fields and list protected by pagesets local_lock in page_alloc.c */
struct per_cpu_pages 
	spinlock_t lock;	/* Protects lists field */
	int count;		/* number of pages in the list */
	int high;		/* high watermark, emptying needed */
	int batch;		/* chunk size for buddy add/remove */
	short free_factor;	/* batch scaling factor during free */
#ifdef CONFIG_NUMA
	short expire;		/* When 0, remote pagesets are drained */
#endif

	/* Lists of pages, one per migrate type stored on the pcp-lists */
	struct list_head lists[NR_PCP_LISTS];
 ____cacheline_aligned_in_smp;

下面为几个关键成员的含义:
count记录了per_cpu缓存中页帧的总数,
high记录了per_cpu缓存中页帧的上限,如果超过这个值就将释放 batch个页帧到伙伴系统中去,如果per_cpu中没有可分配的页帧就从伙伴系统中分配batch个页帧到缓存中来。
per_cpu缓存中的页帧的page就挂接在链表数组struct list_head lists中。

为防止产生过多内存碎片,内核将页帧分类:可移动页,不可移动。内核还定义了一个枚举类型来表示这些可能的类型。

enum migratetype 
	MIGRATE_UNMOVABLE,
	MIGRATE_MOVABLE,
	MIGRATE_RECLAIMABLE,
	MIGRATE_PCPTYPES,	/* the number of types on the pcp lists */
	MIGRATE_HIGHATOMIC = MIGRATE_PCPTYPES,
#ifdef CONFIG_CMA
	/*
	 * MIGRATE_CMA migration type is designed to mimic the way
	 * ZONE_MOVABLE works.  Only movable pages can be allocated
	 * from MIGRATE_CMA pageblocks and page allocator never
	 * implicitly change migration type of MIGRATE_CMA pageblock.
	 *
	 * The way to use it is to change migratetype of a range of
	 * pageblocks to MIGRATE_CMA which can be done by
	 * __free_pageblock_cma() function.
	 */
	MIGRATE_CMA,
#endif
#ifdef CONFIG_MEMORY_ISOLATION
	MIGRATE_ISOLATE,	/* can't allocate from here */
#endif
	MIGRATE_TYPES
;

pcp页帧的类型只可能是:
MIGRATE_UNMOVABLE,
MIGRATE_MOVABLE,
MIGRATE_RECLAIMABLE,

不同类型和大小的pcp页帧挂在不同链表上,所以上面的lists是个数组。

pcp的初始化流程

在内核启动之初per_cpu机制还没有初始化,用于动态分配per_cpu变量的空间还没有分配,所以定义了一个静态的per_cpu变量boot_pageset,用以暂时管理内存域的per_cpu缓存。

mm/page_alloc.c
 
static DEFINE_PER_CPU(struct per_cpu_pageset, boot_pageset);

初始化内存域的zone->pageset字段:

start_kernel

     ------->setup_arch

              ----------->bootmem_init
              
                ------------->zone_sizes_init
                
                    ----------->free_area_init

                          ------------>free_area_init_node
                          
                             ------------->free_area_init_core
                             
                                ------------->zone_init_internals

                                      -------------->zone_pcp_init

以上是关于内核源码解读之内存管理(10)percpu_page_set分析的主要内容,如果未能解决你的问题,请参考以下文章

内核解读之内存管理内存管理三级架构之page

内核解读之内存管理内存管理三级架构之page

内核解读之内存管理内存管理三级架构之page

内核解读之内存管理内存管理三级架构之内存结点node

内核解读之内存管理内存管理三级架构之内存结点node

内核解读之内存管理内存管理三级架构之内存区域zone