nr_free_zone_pages()是用于统计指定高位 zone 范围内空闲页总数的辅助函数,主要用于/proc/meminfo中FreeSwap之外的MemFree/MemAvailable等统计,以及内存回收、水位判断等场景。
核心作用与背景
它回答的问题是:从当前 zone 开始,向上(更高 zone)一共有多少空闲页?参数offset指定了起始 zone 的索引,函数会统计从该 zone 到ZONE_MOVABLE(最高 zone)之间的所有空闲页。
典型实现
static unsigned long nr_free_zone_pages(int offset) { struct zoneref *z; struct zone *zone; /* 只统计比 offset 更高(含)的 zone 的空闲页 */ unsigned long sum = 0; for_each_zone_zonelist_nodemask(zone, z, &contig_zonelist, offset, NULL) { if (!populated_zone(zone)) continue; sum += zone_page_state(zone, NR_FREE_PAGES); } return sum; }(具体实现随版本,核心是遍历 zone 并累加NR_FREE_PAGES。)
关键点解析
1.offset参数的含义
offset是 zone 索引(enum zone_type的值)。函数统计的是从
offset开始、到最高 zone 为止的所有 zone。例如
offset = ZONE_NORMAL,则统计ZONE_NORMAL、ZONE_HIGHMEM、ZONE_MOVABLE等。这与
GFP标志决定的“允许的最高 zone”相反——highest_zoneidx是上限,而offset是下限。
2.contig_zonelist
这是
node_zonelist(0, GFP_KERNEL)或类似的东西,代表从节点 0 开始的 zonelist。使用 zonelist 遍历宏可以正确处理 NUMA 节点关系。
3.zone_page_state(zone, NR_FREE_PAGES)
读取 zone 的
NR_FREE_PAGES计数。这个计数是 per-CPU 差分的汇总值,包含 PCP 缓存和伙伴系统中的空闲页。
4.populated_zone()检查
跳过没有内存的 zone(如某些 NUMA 节点上不存在的
ZONE_HIGHMEM)。
调用场景
它主要被以下函数调用:
1.si_meminfo()/si_mem_available()
用于/proc/meminfo的MemFree等字段,统计从ZONE_NORMAL开始的空闲页。
2.__zone_watermark_ok()的某些变体
用于判断从某个 zone 开始的空闲页是否满足水位。
3. 内存热插拔 / 回收决策
判断高 zone 的空闲页是否足够。
与相关函数的关系
| 函数 | 作用 |
|---|---|
nr_free_zone_pages(offset) | 统计从offset到最高 zone 的空闲页 |
nr_free_buffer_pages() | nr_free_zone_pages(GFP_USER)的封装,统计用户可用页 |
zone_page_state(zone, NR_FREE_PAGES) | 单个 zone 的空闲页 |
总结
nr_free_zone_pages()是按 zone 范围统计空闲页的辅助函数:它遍历从offset指定的 zone 开始到最高 zone 的所有 populated zone,累加NR_FREE_PAGES,返回总空闲页数。它主要用于/proc/meminfo的统计和内存管理决策,是理解系统“有多少可用内存”的基础工具。