zone初始化完毕后,那其中对应的物理页面是如何加入到伙伴系统进行管理的呢?
相关代码文件:mm/nobootmem.cmm/page_alloc.c
__init free_low_memory_core_early函数
![f315f55f9ad92479e20dc0bbd43c94a9.png](https://img-blog.csdnimg.cn/img_convert/f315f55f9ad92479e20dc0bbd43c94a9.png)
free_low_memory_core_early实现
- for_each_free_mem_range遍历所有memblock的region,找到region的起始地址和结束地址然后传给__free_memory_core函数。在__free_memory_core函数中主要调用了__free_pages_memory函数。
__free_pages_memory函数
![6787dced3ff85dddc3ac14499c1058df.png](https://img-blog.csdnimg.cn/img_convert/6787dced3ff85dddc3ac14499c1058df.png)
__free_pages_memory
- 第27行的循环以ORDER为步长来对当前region下可分配的内存进行分配。
- 第28行主要用于计算ORDER,其中_ffs(start)函数的作用是计算start地址中第一次出现1的BIT位是第几位,比如假设start=0xA800,那么_ffs(start)得到的就是8,为什么要这么做,因为start是按页对齐的也就是0x400,而伙伴系统的物理页面是按照2^n次幂来添加的,最大的ORDER为10,也就是最大按2^10次幂页面大小分配,所以第28行就是为了计算当前起始地址下理论上最大可支持分配的物理页面阶层。
- 理解了28行那第30行的循环就一目了然,目的是为了计算当前最大可支持的物理页面分配阶层。
- __free_pages_bootmem函数被调用继续后面的处理在计算出order后
综上,物理页面是按2^ORDER为步长将物理页面加入到伙伴系统中的。而完成这项工作的函数主要由__free_pages_bootmem函数来完成,在其中又主要调用页面释放函数_free_pages来完成这项工作。它的核心功能就是把页面添加到伙伴系统中适当的free_area链表中,释放时会顺便查阅相邻的内存块是否也空闲,如果有空闲就合并然后添加到更高阶层的free_area链表中。
物理页面的释放
__free_pages_bootmem函数
![0916ccc61aa1965565e0183bef0a87c8.png](https://img-blog.csdnimg.cn/img_convert/0916ccc61aa1965565e0183bef0a87c8.png)
__free_pages_bootmem
- 在__free_pages_bootmem函数中对物理页面的释放主要看_free_pages函数
_free_pages函数
![363f0fbd3094d128958ddcbc59a0f17e.png](https://img-blog.csdnimg.cn/img_convert/363f0fbd3094d128958ddcbc59a0f17e.png)
_free_pages
- 在_free_pages函数中分了两种情况,一种是ORDER为0时对页面的释放,它主要是针对特殊情况的页面释放。另一种就是正常情况的释放。先看正常情况
__free_pages_ok函数
![1cae389c15e0644849a08168bc65170e.png](https://img-blog.csdnimg.cn/img_convert/1cae389c15e0644849a08168bc65170e.png)
__free_pages_ok
- 在__free_pages_ok函数中82~84行找到了并设置了页面对应的migratetype属性信息。而对物理页面的释放关键在free_one_page->__free_one_page函数。
__free_one_page
![ea7bd2dc0b67120f79873fe9be98e3d9.png](https://img-blog.csdnimg.cn/img_convert/ea7bd2dc0b67120f79873fe9be98e3d9.png)
__free_one_page实现1
- 第97行计算所属pageblock的索引
- 第98~118行循环结构就是判断在伙伴系统中是否存在与是否页面相邻的内存块,如果存在就合并为更大的内存块,将其加入更高阶层的空闲链表中。其中第99行在伙伴系统中查找是否有相邻内存块,第101行判断是否与当前待释放页面ORDER、ZONE等是否一致。如果是将其从伙伴系统的当前LRU下摘下以便和待释放页面合并到更高阶层的free_area空闲链表中。
![f4b4561b33dd427dfd35676b5ec0c829.png](https://img-blog.csdnimg.cn/img_convert/f4b4561b33dd427dfd35676b5ec0c829.png)
__free_one_page实现2
- 第114~117行合并两个相邻的内存块,ORDER++继续寻找是否存在更大的相邻的内存块。
- 最后134行将最终合并的内存块添加到伙伴系统对应阶层的free_area空闲链表中
正常情况的页面释放分析完毕,回到特殊情况下的页面释放,当ORDER == 0时调用free_hot_cold_page函数来释放页面。
free_hot_cold_page函数
![8d21324e2ef87c1709bbd5598e63907a.png](https://img-blog.csdnimg.cn/img_convert/8d21324e2ef87c1709bbd5598e63907a.png)
free_hot_cold_page实现
- 在zone中之前有讲到一个变量pageset是为每个CPU初始化一个percpu变量,151~155行就是将ORDER==0下的页面释放到这个变量对应的链表下
- per_cpu_pages变量中high的含义如下↓,当per_cpu_pages中的页面高于high时就会调用free_pcppages_bulk函数并最终还是调用__free_one_page函数回收页面到伙伴系统中。
struct per_cpu_pages数据结构
![a04e56ccf237b6ea4a2a5e15c92327f9.png](https://img-blog.csdnimg.cn/img_convert/a04e56ccf237b6ea4a2a5e15c92327f9.png)
struct per_cpu_pages数据结构
- 其中count表示在当前zone中per_cpu_pages的页面数量;high表示当缓存的页面高于high时会回收页面到伙伴系统中;batch表示一次回收到伙伴系统页面的数量
其大致的流程框架图如下:
![f81d8ad859e1e43cbcf50ddf3175eaab.png](https://img-blog.csdnimg.cn/img_convert/f81d8ad859e1e43cbcf50ddf3175eaab.png)
物理页面加入伙伴系统的流程