启动阶段的 PCP(Per-CPU Pages)初始化接口、boot pageset 的常量,以及每 CPU 的 boot 页集和统计结构。
一、per_cpu_pages_init()前置声明
static void per_cpu_pages_init(struct per_cpu_pages *pcp, struct per_cpu_zonestat *pzstats);
前置声明:告诉编译器这个函数存在,实际定义在后面。
作用:初始化一个
per_cpu_pages结构及其配套的per_cpu_zonestat。static:文件内私有。调用时机:在 CPU 上线、zone 初始化时被调用,用于建立该 CPU 对某 zone 的 PCP 缓存。
二、boot pageset 的常量
/* These effectively disable the pcplists in the boot pageset completely */ #define BOOT_PAGESET_HIGH 0 #define BOOT_PAGESET_BATCH 1
作用
这两个常量用于配置启动阶段的 PCP 缓存,使其实际上被禁用。
为什么启动阶段要禁用 PCP?
启动早期,内存管理子系统还没完全初始化好:
PCP 的批量补充/归还机制依赖伙伴系统、水位判断等;
如果此时启用 PCP 缓存,可能因缓存页未及时归还而导致水位判断失准,甚至死锁。
禁用方式:
HIGH = 0:高水位为 0,意味着 PCP 缓存中不能有任何页——一旦有页就立即触发批量归还。BATCH = 1:批量大小为 1,每次只补充/归还一页。
效果:PCP形同虚设,分配/释放几乎直接走伙伴系统,行为最简单、最安全。
与正常运行期的对比
| 阶段 | pcp->high | pcp->batch | PCP 行为 |
|---|---|---|---|
| 启动(boot pageset) | 0 | 1 | 禁用,直接走伙伴系统 |
| 正常运行 | 动态计算(几十~几百) | 动态计算 | 缓存页,减少锁竞争 |
三、boot pageset 的 per-CPU 结构
static DEFINE_PER_CPU(struct per_cpu_pages, boot_pageset); static DEFINE_PER_CPU(struct per_cpu_zonestat, boot_zonestats);
1.boot_pageset
每 CPU 一个
struct per_cpu_pages,是启动阶段使用的 PCP 缓存。每个 zone 在初始化时,会先指向这个 boot pageset,等真正初始化时再切换到分配好的 pageset。
用
DEFINE_PER_CPU定义,每 CPU 一份。
2.boot_zonestats
每 CPU 一个
struct per_cpu_zonestat,是启动阶段的 zone 统计结构。per_cpu_zonestat保存 per-CPU 的 zone 统计(如NR_FREE_PAGES的差分值、NR_ZONE_*计数等)。同样每 CPU 一份。
为什么需要单独的 boot 版本?
启动顺序问题:
zone->per_cpu_pageset需要指向有效的 per-CPU 结构,但动态分配的 pageset 在内存子系统完全就绪前可能还不可用。解决方案:先用静态定义的
boot_pageset/boot_zonestats作为占位,让 zone 能正常工作;等setup_per_cpu_pageset()执行时,再切换到动态分配的正式 pageset。静态定义:
DEFINE_PER_CPU在编译期分配空间,不依赖运行时内存分配,启动早期即可用。
切换流程(示意)
/* 启动早期:zone 指向 boot pageset */ zone->per_cpu_pageset = &boot_pageset; /* 稍后 setup_per_cpu_pageset():切换到正式 pageset */ for_each_online_cpu(cpu) { pcp = per_cpu_ptr(zone->per_cpu_pageset, cpu); per_cpu_pages_init(pcp, pzstats); // 用正式 pageset 重新初始化 }四、与前面内容的呼应
| 前面出现过的 | 这里的关联 |
|---|---|
struct per_cpu_pages | boot pageset 的类型 |
struct per_cpu_zonestat | boot zonestats 的类型 |
pcp->high/pcp->batch | 被BOOT_PAGESET_HIGH/BOOT_PAGESET_BATCH控制 |
free_pcppages_bulk() | 高水位为 0 时会频繁触发,但启动期 batch=1 影响小 |
drain_pages()/drain_all_pages() | PCP 排空机制,与 pageset 初始化配套 |
setup_per_cpu_pageset() | 切换到正式 pageset 的初始化函数 |
五、小结表
| 项 | 说明 |
|---|---|
per_cpu_pages_init() | 前置声明,初始化 PCP + zonestat |
BOOT_PAGESET_HIGH = 0 | 启动阶段 PCP 高水位为 0,禁用缓存 |
BOOT_PAGESET_BATCH = 1 | 启动阶段批量大小为 1 |
boot_pageset | 每 CPU 的启动阶段 PCP 结构 |
boot_zonestats | 每 CPU 的启动阶段 zone 统计结构 |
| 设计意图 | 启动早期用静态占位结构,避免依赖动态分配;等初始化完成再切换到正式 pageset |
一句话总结
这段代码为启动阶段的 PCP 机制做准备:
per_cpu_pages_init()是初始化接口;BOOT_PAGESET_HIGH=0/BOOT_PAGESET_BATCH=1使 boot pageset 的 PCP 缓存形同禁用,分配/释放几乎直接走伙伴系统,避免启动早期的不稳定;