文章目录
系统内核初始完毕后,使用页分配器管理物理页,使用的页分配器是伙伴分配器,特点是算法简单高效。
1. 分区的伙伴分配器
1.1 概念
- 页块(page block):连续的物理页。
- 阶(order): 伙伴分配器的专业术语,页的数量单位。2^n 个连续页为n阶页块。
- 伙伴: 满足以下两个n阶页块称为伙伴
- 两个页块是相邻的,即物理地址是连续的。
- 页块的第一页的物理页必须的2^n的整数倍。
- 如果合并成(n + 1)阶页块,第一页的物理页号必须是2^(n + 1) 的整数倍。
1.2 分配n阶页块过程
伙伴分配器分配和释放物理页的数量单位为阶。
1.3 分区伙伴分配器
内核在基本的伙伴分配器基础上改进扩展,支持内存节点和区域,称为分区的伙伴分配器(zone buddy allocator);为了预防内存碎片,把物理根据可移动性分组;针对分配,单页做了性能优化,为了减少处理器之间的锁竞争,在内存区域增加1个每处理器页集合。
1.3.1 数据结构
分区的伙伴分配器专注于某个内存节点的某个区域,内存区域的结构体成员free_area用来维护空闲页块,数组下标对应页块的阶数。
源码分析:
free_area结构:
1.3.2 根据分配标志获取首选区域类型
申请页时,最低4个标志位用来指定首选内存区域类型。源码如下:
标志组合:
内存区域类型:
内核使用宏GFP_ZONE_TABLE定义了标志组合到区域类型的映射表,其中GFP_ZONES_SHIFT是区域类型占用的位数,GFP_ZONE_TABLE把每种标志组合映射到32位整数的某个位置,偏移是(标志组合*区域类型位数),从这个偏移开始的GFP_ZONES_SHIFT个二进制位存放区域类型。编译器编译阶段已经优化计算出结果。
1.3.3 备用区域列表
如果首选的内存节点区域不能满足分配的请求,可以从备用的内存区域借用物理页。借用必须遵守相应的规则。
- 一个内存节点的某个区域类型可以从另一个内存节点的相同区域类型借用物理页,比如节点0的普通区域可以从节点1的普通区域借用物理页。
- 高区域类型可以从低区域类型借用物理页,比如普通区域可以从DMA区域借用物理页;
- 低区域类型不能从高区域类型借用物理页。比如DMA区域不能从普通区域借用物理页。
内存节点的pg_data_t实例已定义备用区域列表,源码如下:
UMA系统只有一个备用区域列表,按区域类型从高到低排序。假设UMA系统包含普通区域和DMA区域,那么备用区域列表:{普通区域, DMA区域}。UMA系统每个内存节点有两个备用区域列表:一个包含所有内存节点的区域,另一个只包含当前内存节点的区域。
包含所有内存节点的备用区域列表有两种排序方法:
- 节点优先顺序:先根据节点距离从小到大排序,在每个节点里面根据区域类型从高到低排序。优点是优先选择距离近的内存,缺点是在高区域耗尽以前使用的低区域。
- 区域优先顺序:先根据区域类型从高到低排序,然后再每个区域类型里面根据节点距离从小到大排序。优点是减少低区域耗尽的概率,缺点是不能保证优先选择距离近的内存。
- 默认的排序方法会自动选择最优的排序方法:比如64位系统,因为需要DMA和DMA32区域的备用相对少,所以选择节点优先顺序;如果是32位系统,选择区域优先顺序。
1.3.4 区域水线
首选的内存区域什么情况下会从备用区域借用物理页呢?每个内存区域有3个水线。
- 高水线(high): 如果内存区域的空闲页数大于高水线,说明内存区域的内存充足;
- 低水线(low): 如果内存区域的空闲页数小于低水线,说明内存区域的内存轻微不足;
- 最低水线(min): 如果内存区域的空闲页数小于最低水线,说明内存区域的内存严重不足。最低水线以下的内存称为紧急保留内存,在内存严重不足的紧急情况下,给承诺“分给我们少量的紧急保留内存使用,我可以释放更多的内存”的进程使用。
数据结构如下:
1.3.5 watermark水位控制
内核源码重要数据参数
三个水位值如何计算出来?
unsigned long managed_pages; // 伙伴分配器管理的物理页的数量
// 代表的是zone中的所有页,包含空洞,计算公式:zone_end_pfn-zone_start_pfn
unsigned long spanned_pages; // 当前区域跨越的总页数,包括空洞
// 代表zone中可用的所有物理页,计算公式:spanned_pages-hole_pages
unsigned long present_pages; // 当前区域存在的物理页的数量,不包括空洞
// 代表通过buddy管理所有可用的页,计算公式:present_pages-reserved_+pages
三者之间的关系?
spanned_pages > present_pages > managed_pages
- min_free_kbytes 代表的是系统保留空闲内存的最低限。watermark[WMARK_MIN]的值是通过min_free_kbytes计算出来的。
2. 分配页
linux内核中,所有分配页的函数最终都会调用到_alloc_pages_nodemask,此函数为分区的伙伴分配器的心脏。
算法流程:
- 根据分配标志为得到首选区域类型和迁移类型;
- 执行快速路径,使用低水线尝试第一次分配;
- 如果快速路径分配失败,才执行慢速路径。
2.1 核心函数
2.1.1 分配标志位分析
2.1.2 快速路径调用函数
2.1.3 慢速路径调用函数
3. 释放页
页分配器提供的释放接口,源码如下:
参考
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,点击立即学习:https://course.0voice.com/v1/course/intro?courseId=5&agentId=0