背景
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. 介绍
在(四)Linux内存模型之Sparse Memory Model中,我们分析了bootmem_init函数的上半部分,这次让我们来到下半部分吧,下半部分主要是围绕zone_sizes_init函数展开。
前景回顾:
bootmem_init()函数代码如下:
void __init bootmem_init(void)
{
unsigned long min, max;
min = PFN_UP(memblock_start_of_DRAM());
max = PFN_DOWN(memblock_end_of_DRAM());
early_memtest(min << PAGE_SHIFT, max << PAGE_SHIFT);
max_pfn = max_low_pfn = max;
arm64_numa_init();
/*
* Sparsemem tries to allocate bootmem in memory_present(), so must be
* done after the fixed reservations.
*/
arm64_memory_present();
sparse_init();
zone_sizes_init(min, max);
memblock_dump_all();
}
在Linux中,物理内存地址区域采用zone来管理。不打算来太多前戏了,先上一张zone_sizes_init的函数调用图吧:
需要再说明一点是,使用的是ARM64,UMA(只有一个Node),此外,流程分析中那些没有打开的宏,相应的函数就不深入分析了。开始探索吧!
2. 数据结构
关键的结构体如上图所示。
在NUMA架构下,每一个Node都会对应一个struct pglist_data,在UMA架构中只会使用唯一的一个struct pglist_data结构,比如我们在ARM64 UMA中使用的全局变量struct pglist_data __refdata contig_page_data。
struct pglist_data 关键字段
struct zone node_zones[]; //对应的ZONE区域,比如ZONE_DMA,ZONE_NORMAL等
struct zonelist_node_zonelists[];
unsigned long node_start_pfn; //节点的起始内存页面帧号
unsigned long node_present_pages; //总共可用的页面数
unsigned long node_spanned_pages; //总共的页面数,包括有空洞的区域
wait_queue_head_t kswapd_wait; //页面回收进程使用的等待队列
struct task_struct *kswapd; //页面回收进程
struct zone 关键字段
unsigned long watermark[]; //水位值,WMARK_MIN/WMARK_LOV/WMARK_HIGH,页面分配器和kswapd页面回收中会用到
long lowmem_reserved[]; //zone中预留的内存
struct pglist_data *zone_pgdat; //执行所属的pglist_data
struct per_cpu_pageset *pageset; //Per-CPU上的页面,减少自旋锁的争用
unsigned long zone_start_pfn; //ZONE的起始内存页面帧号
unsigned long managed_pages; //被Buddy System管理的页面数量
unsigned long spanned_pages; //ZONE中总共的页面数,包含空洞的区域
unsigned long present_pages; //ZONE里实际管理的页面数量
struct frea_area free_area[]; //管理空闲页面的列表
宏观点的描述:struct pglist_data描述单个Node的内存(UMA架构中的