【原创】(七)Linux内存管理 - zoned page frame allocator - 2
背景
Read the fucking source code!
--By 鲁迅A picture is worth a thousand words.
--By 高尔基
说明:
- Kernel版本:4.14
- ARM64处理器,Contex-A53,双核
- 使用工具:Source Insight 3.5, Visio
1. 概述
本文将分析Buddy System
。
Buddy System
伙伴系统,是通过将物理内存划分为页面来进行管理的系统,支持连续的物理页面分配和释放。此外,使用与碎片相关的算法来确保最大的连续页面。
先通过一个例子大体介绍一下原理吧:
空闲的物理页框按大小分组成0~MAX_ORDER
个链表,每个链表存放页框的大小为2的n次幂,其中n在0 ~ MAX_ORDER-1
中取值。
假设请求分配2^8 = 256
个页框块:
- 检查
n = 8
的链表,检查是否有空闲块,找到了则直接返回; - 没有找到满足需求的,则查找
n = 9
的链表,找到512大小
空闲块,拆分成两个256大小
块,将其中一个256大小
块返回,另一个256大小
块添加到n = 8
的链表中; - 在
n = 9
的链表中没有找到合适的块,则查找n = 10
的链表,找到1024大小空闲块,将其拆分成512 + 256 + 256
大小的块,返回需要获取的256大小
的块,将剩下的512大小
块插入n = 9
链表中,剩下的256大小
块插入n = 8
的链表中;
合并过程是上述流程的逆过程,试图将大小相等的Buddy块
进行合并成单独的块,并且会迭代合并下去,尝试合并成更大的块。合并需要满足要求:
- 两个
Buddy块
大小一致; - 它们的物理地址连续;
- 第一个
Buddy块
的起始地址为(2 x N x 4K)
的整数倍,其中4K
为页面大小,N
为Buddy块
的大小;
struct page
结构中,与Buddy System
相关的字段有:
_mapcount
: 用于标记page
是否处在Buddy System
中,设置成-1
或PAGE_BUDDY_MAPCOUNT_VALUE(-128)
;private
: 一个2^k
次幂的空闲块的第一个页描述符中,private
字段存放了块的order
值,也就是k
值;index
: 存放MIGRATE
类型;_refcount
: 用户使用计数值,没有用户使用为0,有使用的话则增加;
合并时如下图所示:
2. Buddy页面分配
Buddy页面分配的流程如下图所示:
从上图中可以看出,在页面进行分配的时候,有以下四个步骤:
- 如果申请的是
order = 0
的页面,直接选择从pcp
中进行分配,并直接退出; order > 0
时,如果分配标志中设置了ALLOC_HARDER
,则从free_list[MIGRATE_HIGHATOMIC]
的链表中进行页面分配,分配成功则返回;- 前两个条件都不满足,则在正常的
free_list[MIGRATE_*]
中进行分配,分配成功则直接则返回; - 如果3中分配失败了,则查找
后备类型fallbacks[MIGRATE_TYPES][4]
,并将查找到的页面移动到所需的MIGRATE
类型中,移动成功后,重新尝试分配;
如下图:
上述分配的过程,前3个步骤都会调用到__rmqueue_smallest
,第4步调用__rmqueue_fallback
,将从这两个函数来分析。
2.1 __rmqueue_smallest
__rmqueue_smallest
的源代码比较简单,贴上来看看吧:
static inline
struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
int migratetype)
{
unsigned int current_order;
struct free_area *area;
struct page *page;
/* Find a page of the appropriate size in the preferred list */
for (current_order = order; current_order < MAX_ORDER; ++current_order) {
area = &(zone->free_area[current_order]);
page = list_first_entry_or_null(&area->free_list[migratetype],
struct page, lru);
if (!page)
continue;
list_del(&page->lru);
rmv_page_order(page);
area->nr_free--;
expand(zone, page, order, current_order, area, migratetype);
set_pcppage_migratetype(page, migratetype);
return page;
}
return NULL;
}
从代码中可以看出:
- 从申请的
order
大小开始查找目标MIGRATE
类型链表中页表,如果没有找到,则从更大的order
中查找,直到MAX_ORDER
; - 查找到页表之后,从对应的链表中删除掉,并调用
expand
函数进行处理;
expand
函数的处理逻辑就跟本文概述中讲的例子一样,当在大的order
链表中申请到了内存后,剩余部分会插入到其他的order
链表中,来一张图就清晰了:
2.2 __rmqueue_fallback
当上述过程没有分配到内存时,便会开始从后备迁移类型中进行分配。
其中,定义了一个全局的二维fallbacks
的数组,并根据该数组进行查找,代码如下:
/*
* This array describes the order lists are fallen back to when
* the free lists for the desirable migrate type are depleted
*/
static int fallbacks[MIGRATE_TYPES][4] = {
[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_TYPES },
[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_TYPES },
[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_TYPES },
#ifdef CONFIG_CMA
[MIGRATE_CMA] = { MIGRATE_TYPES }, /* Never used */
#endif
#ifdef CONFIG_MEMORY_ISOLATION
[MIGRATE_ISOLATE] = { MIGRATE_TYPES }, /* Never used */
#endif
};
__rmqueue_fallback
完成的主要工作就是从后备fallbacks
中找到一个迁移类型页面块,将其移动到目标类型中,并重新进行分配。
下图将示例整个流程:
3. Buddy页面释放
页面释放是申请的逆过程,相对来说要简单不少,先看一下函数调用图吧:
当order = 0
时,会使用Per-CPU Page Frame
来释放,其中:
MIGRATE_UNMOVABLE, MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE
三个按原来的类型释放;MIGRATE_CMA, MIGRATE_HIGHATOMIC
类型释放到MIGRATE_UNMOVABLE
类型中;MIGRATE_ISOLATE
类型释放到Buddy系统中;
此外,在PCP释放的过程中,发生溢出时,会调用free_pcppages_bulk()
来返回给Buddy系统。来一张图就清晰了:
在整个释放过程中,核心函数为__free_one_page
,该函数的核心逻辑部分如下所示:
continue_merging:
while (order < max_order - 1) {
buddy_pfn = __find_buddy_pfn(pfn, order);
buddy = page + (buddy_pfn - pfn);
if (!pfn_valid_within(buddy_pfn))
goto done_merging;
if (!page_is_buddy(page, buddy, order))
goto done_merging;
/*
* Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page,
* merge with it and move up one order.
*/
if (page_is_guard(buddy)) {
clear_page_guard(zone, buddy, order, migratetype);
} else {
list_del(&buddy->lru);
zone->free_area[order].nr_free--;
rmv_page_order(buddy);
}
combined_pfn = buddy_pfn & pfn;
page = page + (combined_pfn - pfn);
pfn = combined_pfn;
order++;
}
__find_buddy_pfn
: 根据释放页面的pfn
计算对应的buddy_pfn
,比如pfn = 0x1000, order = 3
,则buddy_pfn = 0x1008
,pfn = 0x1008, order = 3
,则buddy_pfn = 0x1000
;page_is_buddy
:将page
和buddy
进行配对处理,判断是否能配对;- 进行combine之后,再将pfn指向合并后的开始位置,继续往上一阶进行合并处理;
按照惯例,再来张图片吧:
不得不说,还有很多细节没有去扣,一旦沉沦,将难以自拔,待续吧。
【原创】(七)Linux内存管理 - zoned page frame allocator - 2的更多相关文章
- 【原创】(六)Linux内存管理 - zoned page frame allocator - 1
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- 【原创】(八)Linux内存管理 - zoned page frame allocator - 3
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- 【原创】(九)Linux内存管理 - zoned page frame allocator - 4
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- 【原创】(十)Linux内存管理 - zoned page frame allocator - 5
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- Linux内存管理 (11)page引用计数
专题:Linux内存管理专题 关键词:struct page._count._mapcount.PG_locked/PG_referenced/PG_active/PG_dirty等. Linux的内 ...
- 【原创】(十四)Linux内存管理之page fault处理
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- Linux内存管理6---伙伴算法与slab
1.前言 本文所述关于内存管理的系列文章主要是对陈莉君老师所讲述的内存管理知识讲座的整理. 本讲座主要分三个主题展开对内存管理进行讲解:内存管理的硬件基础.虚拟地址空间的管理.物理地址空间的管理. 本 ...
- [转帖]Linux分页机制之分页机制的演变--Linux内存管理(七)
Linux分页机制之分页机制的演变--Linux内存管理(七) 2016年09月01日 20:01:31 JeanCheng 阅读数:4543 https://blog.csdn.net/gatiem ...
- Linux内存描述之内存页面page–Linux内存管理(四)
服务器体系与共享存储器架构 日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.7 X86 & arm gatieme LinuxDeviceDriver ...
随机推荐
- poj 1182 食物链(种类并查集 ‘初心者’)
题目链接:http://poj.org/problem?id=1182 借着这题可以好好理解一下种类并查集,这题比较简单但挺经典的. 题意就不解释了,中问题. 关于种类并查集结局方法也是挺多的 1扩增 ...
- 如何将 JavaScript 代码添加到网页中,以及 <script> 标签的属性
Hello, world! 本教程的这一部分内容是关于 JavaScript 语言本身的. 但是,我们需要一个工作环境来运行我们的脚本,由于本教程是在线的,所以浏览器是一个不错的选择.我们会尽可能少地 ...
- Linux执行后台work相关
Linux的后台运行.关闭.查看后台任务 & ctrl+z jobs fg bg kill nohup setsid disown screen 1.& 加在命令的最后,可以把命令放到 ...
- .NET Core 3.0 Preview 9 发布
翻译自官方博客 今天,我们宣布推出.NET Core 3.0 Preview 9.就像预览版8一样,我们专注打磨最终版本的.NET Core 3.0,而不是添加新功能.如果这些最终版本看起来不像早期预 ...
- Java高性能编程之CAS与ABA及解决方法
Java高性能编程之CAS与ABA及解决方法 前言 如果喜欢暗色调的界面或者想换换界面,可以看看我在个人博客发布的 Java高性能编程之CAS与ABA及解决方法. CAS概念 CAS,全称Compar ...
- 阿里mysql规范
(一)建表规约 1.[强制]表达是与否概念的字段,必须使用 is_xxx的方式命名,数据类型是 unsigned tinyint( 1表示是,0表示否),此规则同样适用于 odps建表. 说明:任何字 ...
- 漫谈Java中的OOPS
什么是OOPS? 面向对象编程是一种编程概念,其工作原理是对象是程序中最重要的部分.它允许用户创建他们想要的对象,然后创建处理这些对象的方法.操作这些对象以获得结果是面向对象编程的目标. 面向对象编程 ...
- jmeter 命令压测生成报告
1.本地复制到远程 scp -r local_folder remote_username@remote_ip:remote_folder 或者 scp -r local_folder remote_ ...
- Kylin配置Spark并构建Cube
HDP版本:2.6.4.0 Kylin版本:2.5.1 机器:三台 CentOS-7,8G 内存 Kylin 的计算引擎除了 MapReduce ,还有速度更快的 Spark ,本文就以 Kylin ...
- liunx帮助whatis使用方法
liunx帮助 获取帮助的能力决定了技术的能力! 1.whatis ♢只显示命令的简短描述,相对功能比较单一. [09:42:22 root@centos ~]# ...