内核解读之内存管理页分配器伙伴系统介绍(代码片段)

奇妙之二进制 奇妙之二进制     2023-01-11     402

关键词:

文章目录

1 分配器的需求

伙伴系统是linux的页框分配器,负责系统物理内存的分配工作。由于几乎所有模块的运行都依赖于内存,而不同模块对内存分配策略又有不同的需求,因此如何高效地满足这些需求就成为了页框分配器的核心问题。

例如对于大部分内存分配请求,若内存分配器暂时无法满足要求则允许其返回失败。而对于一些内核关键路径的请求,一旦失败可能会导致内核无法继续运行,则这种分配并不允许失败。

对于实时性要求不高的模块,若分配器当前空闲内存不足,可以将进程睡眠,然后通过swap页面交换或cache/buffer内存回收机制从系统回收部分内存后再延迟分配。而有些处于关中断等临界区的代码不允许睡眠,因此这种分配一旦空闲内存不足,则应该立即返回分配失败,而不能睡眠等待。

前面提到内存回收,那什么时候应该触发内存回收,回收操作又应该在什么时候完成。对于不允许失败的操作,即使通过回收还无法得到足够多的内存又该如何处理。

除此以外,由于内存分配是一种比较频繁的操作,分配器还需要考虑内存分配的速度,以期使其能尽快完成。同时,cache缓存能极大地提高程序的执行效率,若在内存分配策略中考虑优先分配还处于cache中的hot page,显然能够提升内存访问的速度。

以上这些问题都需要在内存分配策略中综合考虑。当然,linux在不同场景下对内存分配策略的需求千差万别,伙伴系统分配器也是力求能够尽量满足大部分的通用需求,其中很多参数也是根据经验值设置。而且随着需求的变化,分配器本身也会处于不断更新之中。

2 伙伴系统的内存组织

内存分配的首要任务当然是如何快速找到待分配的空闲内存,伙伴系统通过以下三个层次管理系统的空闲页框:
(1)为每个内存节点建立内存分配的zonelist优先级列表
(2)通过freelist管理每个zone中的空闲内存,这些内存页框按2的指数次分为不同的order,并且每个order进一步按迁移类型不同挂到独立的链表中
(3)内存zone为每个cpu维护一个per_cpu_pages数据结构,用于保存该cpu的hot page。若分配的页面数量较少,且per_cpu_pages中拥有合适的页面,则优先从pcp中分配内存,以提升系统的性能

2.1 zonelist列表结构

linux通过node和zone划分系统内存,并且为每个node建立了按优先级排列的zonelist列表。由于cpu对与其绑定的local node访问速度最快,而对与其距离最远的remote node访问速度最慢。因此node优先级主要考虑给定node与local node的距离,同时也会根据一些其它因素做适当的调整。

内核又根据内存的应用场景不同将每个node的内存划分为不同的zone,对于arm64架构一般包含zone_normal和zone_dma两种zone。其中zone_dma是可被用于dma的内存,因此对系统来说更加重要,在内存分配时除非明确指定从该zone分配,否则只有在zone_normal内存不足时才会动用这部分资源。

在上一篇伙伴系统初始化中已经详细介绍了内核zonelist的建立过程,其在建立完成后每个node都会拥有一个类似下图的内存分配优先级列表:

例如对于上图的node 0节点,若需要分配非DMA的普通内存,则其先尝试从从node 0的normal zone分配,若分配不成功,则继续尝试从node 0的DMA zone分配,若依然不成功,则fallback到node 1中分配,依次类推。

对于numa系统,除了上面的这张zonelist之外,还会为每个节点创建一张不带fallback的zonelist。若申请者明确要求只从某个节点分配内存,则若该节点分配失败后不能fallback到备用节点,因此这张表只包含节点自身的zone,其形式如下:

以上结构在内核中通过数据结构struct zonelist表示,其定义如下:

struct zonelist 
	struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1];
;

其中MAX_ZONES_PER_ZONELIST表示一个node最多可以包含多少个可用于内存分配的zone,其定义如下:

#define MAX_ZONES_PER_ZONELIST (MAX_NUMNODES * MAX_NR_ZONES)

而结构体zoneref的定义如下:

struct zoneref 
	struct zone *zone;
	int zone_idx;
;

它表示一个特定的zone,其中zone指向特定zone的指针,zone_idx表示该zone的类型,如normal zone或dma zone等

2.2 zone的空闲内存结构

伙伴系统在一个特定的zone中将物理页框按2的order次为单位进行划分,并且相同order的页框继续按不同的迁移类型进一步划分,划分后的每种类型页框被挂到不同的空闲链表上。在内存分配时,根据调用接口传入的order和migrate_type参数,就能快速找到对应的链表,若该链表非空则内存分配完成。以下为一个zone的空闲页框链表示意图:

若需要分配一个order为1的可移动页框,则直接获取上图蓝色链表第一个节点的页框即可。
zone中使用下述结构体定义空闲内存链表:

struct free_area	free_area[MAX_ORDER];
struct free_area 
	struct list_head	free_list[MIGRATE_TYPES];
	unsigned long		nr_free;
;

其中free_list表示特定order中不同迁移类型内存的空闲链表,nr_free表示该order下总的空闲页框数量

2.3 内存迁移类型

2.4 pcp页框

相对于访问寄存器而言,cpu对内存的访问是相当耗时的,因此为了提高系统性能,处理器都在cpu和内存之间增加一些更快的cache作为临时缓存。cache一般会缓存最近访问过的内存数据,因此最近被使用过的页位于cache中的概率也最大。

伙伴系统的页框分配策略中,若能优先分配最后被释放的内存页,则这些页还位于cache中的概率也比较大。因此伙伴系统为每个cpu维护了一个per_cpu_pages数据结构,用于保存最近被释放的页框,在内存分配时也会优先从该列表中查找匹配的页框,从而尽量提升系统的性能。

与free_area类似,这些页框也是根据order和迁移类型划分的。为了减轻其维护的页框数量,pcp只包含了order较低的几种页框长度,即其只维护小于PAGE_ALLOC_COSTLY_ORDER的页框,在当前内核版本中该值被定义为3。其结构体定义如下:

/* 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;

pcp也是安装迁移类型挂接在不同链表,并且每种类型都有0,1,… PAGE_ALLOC_COSTLY_ORDER 不同阶的大小。

/*
 * One per migratetype for each PAGE_ALLOC_COSTLY_ORDER. One additional list
 * for THP which will usually be GFP_MOVABLE. Even if it is another type,
 * it should not contribute to serious fragmentation causing THP allocation
 * failures.
 */
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
#define NR_PCP_THP 1
#else
#define NR_PCP_THP 0
#endif
#define NR_LOWORDER_PCP_LISTS (MIGRATE_PCPTYPES * (PAGE_ALLOC_COSTLY_ORDER + 1))
#define NR_PCP_LISTS (NR_LOWORDER_PCP_LISTS + NR_PCP_THP)

除了链表成员以外,该结构体还包含了count、high和batch等成员。我们知道pcp主要是为了优化性能而在free_area之上提供了一层类似缓存的机制,若其包含的页框数量太多则最先释放的那些页框可能早已不在cache中,从而失去了其本身的目的。更糟糕的是,这些缓存在pcp中的页框,若释放到free_area中则可能可以与其它页框合并成order更高的内存块,从而减少系统的内存碎片。

我们知道内核中连续大块内存是稀缺资源,因此为了减轻内存碎片,需要控制pcp中内存的数量。high参数就是用于该目的,当pcp中的内存高于high时,其会将一部分内存释放回buddy的free_area中,当然若pcp中的内存不足,也会从free_area中分配部分内存。那么每次释放或分配内存的数量是多少呢?它是由batch参数指定的,其表示单次分配或释放的页框数为batch* 2 ^order个。

最后,我们再看一下链表操作需要的注意点。由于我们每次都希望从pcp中分配最新加入的页框,因此在向pcp释放页框时需要将其插入到链表头后面,而分配页框时也需要从链表头后第一个节点处开始分配,以达到获取hot page的目的。以下为pcp内存释放时链表节点插入和内存分配时链表节点删除示意图:

内核解读之内存管理页分配器伙伴系统介绍(代码片段)

文章目录1分配器的需求2伙伴系统的内存组织2.1zonelist列表结构2.2zone的空闲内存结构2.3内存迁移类型2.4pcp页框1分配器的需求伙伴系统是linux的页框分配器,负责系统物理内存的分配工作。由于几乎所有模... 查看详情

内核解读之内存管理内存管理三级架构之page(代码片段)

...级架构,node->zone->page。本节就来介绍page。页是内核管理内存的基本单位,体系结构不同,支持的页大小也不尽相同,还有些体系结构甚至支持几种不同的页大小。大多数32位体系结构支持4KB的页,而64位... 查看详情

内核解读之内存管理内存管理三级架构之page(代码片段)

...级架构,node->zone->page。本节就来介绍page。页是内核管理内存的基本单位,体系结构不同,支持的页大小也不尽相同,还有些体系结构甚至支持几种不同的页大小。大多数32位体系结构支持4KB的页,而64位... 查看详情

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

...争激烈程度也会越来越大。为了改善这个问题,linux内核中引入了per_cpu_page 查看详情

内核解读之内存管理内存管理三级架构之page(代码片段)

...级架构,node->zone->page。本节就来介绍page。页是内核管理内存的基本单位,体系结构不同,支持的页大小也不尽相同,还有些体系结构甚至支持几种不同的页大小。大多数32位体系结构支持4KB的页,而64位... 查看详情

linux内核源码分析之伙伴系统(代码片段)

...免碎片1,依据可移动性组织页2、虚拟可移动内存域内核中不连续页的分配用vmalloc分配内存释放内存伙伴关系基础        free_area[]数组中各个元素的索引也解释为阶,用于指定对应链表中的连续内存区包含多少页帧... 查看详情

内核解读之内存管理内存模型(代码片段)

文章目录1、基本术语2、FLATMEM(平坦内存模型)3、SPARSEMEM稀疏内存模型1、基本术语在介绍内存模型之前需要了解一些基本的知识。1、什么是pageframe?在linux操作系统中,物理内存被分成一页页的pageframe来管理ÿ... 查看详情

内核解读之内存管理(12)进程虚拟内存管理vm_area_struct与反向映射(代码片段)

...径中,无论是伙伴系统中分配页的函数,还是slab分配器中分配对象的函数,它们都会尽量快速地响应内核的分配请求,将相应的内存提交给内核使用,而内核对待用户空间显然不能如此。用户空间动态申请内... 查看详情

内核解读之内存管理(12)进程虚拟内存管理vm_area_struct与反向映射(代码片段)

...径中,无论是伙伴系统中分配页的函数,还是slab分配器中分配对象的函数,它们都会尽量快速地响应内核的分配请求,将相应的内存提交给内核使用,而内核对待用户空间显然不能如此。用户空间动态申请内... 查看详情

内核解读之内存管理内存模型(代码片段)

文章目录基本的术语CONFIG_FLATMEM(平坦内存模型)稀疏的内存模型基本的术语在介绍内存模型之前需要了解一些基本的知识。1、什么是pageframe?在linux操作系统中,物理内存被分成一页页的pageframe来管理,具体... 查看详情

伙伴系统(代码片段)

...一组连续的页而建立的高效分配策略,结合2的幂次方个分配器和空闲缓冲区合并的技术。内存被分成含有若干个(2^0,2^1,2^2...2^11)页面的块。  伙伴系统的分配器维护空闲页面所组成的块,这里每一块都是2的方幂个页面,方幂... 查看详情

内核解读之内存管理内存管理三级架构之内存区域zone(代码片段)

...,一个页框就是一个内存的分配单元,可用于任何事情:存放内核数据,用户数据和缓冲磁盘数据等等。任何种类的数据页都可以存放在任页框中,没有任何限制。但是Linux内核又把各个物理内存节点分成n个不同的管理区域zone,这是为... 查看详情

内存管理(上)(代码片段)

...配大块内存的伙伴系统;分配较小块内存的slab、slub和slob分配器;分配连续内存块的vmalloc机制;进程的地址空间。Linux内核一般将处理器的虚拟地址分为两个部分,以IA-32为例,地址空间在用户进程和内核之间的划分比例为3:1。4... 查看详情

内核解读之内存管理开篇介绍

...目录1、开篇介绍2、基本概念1、开篇介绍内存管理是linux内核比较重要的一个模块,其实也是任何操作系统里的一个核心专题。在实际的开发工作中,经常会遇到和内存牵扯的问题,比如内存泄露啊,内存越界等。如果你的技术... 查看详情

内核解读之内存管理开篇介绍

...目录1、开篇介绍2、基本概念1、开篇介绍内存管理是linux内核比较重要的一个模块,其实也是任何操作系统里的一个核心专题。在实际的开发工作中,经常会遇到和内存牵扯的问题,比如内存泄露啊,内存越界等。如果你的技术... 查看详情

实验2正篇——内存管理(代码片段)

...实际情况理解:      第一部分是内核的物理内存分配器,内核能够分配内存并且能够释放之。分配的内存单元为4k,被称之为内存页。 我们需要实现的任务是维护数据结构用于记录那些内存被使用࿰ 查看详情

内核解读之内存管理内存管理三级架构之内存结点node(代码片段)

文章目录0、概述1、内存节点node0、概述结合NUMA的架构,Linux抽象出了三级内存管理架构:内存节点node、内存区域zone和物理页框page。在NUMA模型中,每个CPU都有自己的本地内存节点(memorynode),而且还可以... 查看详情

内核解读之内存管理内存管理三级架构之内存结点node(代码片段)

文章目录0、概述1、内存节点node2、NUMA的初始化0、概述结合NUMA的架构,Linux抽象出了三级内存管理架构:内存节点node、内存区域zone和物理页框page。在NUMA模型中,每个CPU都有自己的本地内存节点(memorynode)&#x... 查看详情