一、页(Page):内存管理的基本单位
1. 物理页的本质
// 页结构体定义(include/linux/mm_types.h)
struct page {
unsigned long flags; // 页状态标志位
atomic_t _refcount; // 引用计数
union {
struct { /* 匿名页 */ }; // 匿名内存
struct { /* 页缓存 */ }; // 文件缓存
struct { /* Slab */ }; // 内核对象缓存
};
unsigned long private; // 私有数据
struct list_head lru; // LRU链表节点
void *virtual; // 虚拟地址(如果映射)
};
物理表示:每个物理页对应一个 struct page 结构体
大小固定:通常为 4KB(x86/ARM),由 PAGE_SHIFT=12 定义
全局数组:所有物理页通过 mem_map 数组统一管理
2. 页的三种核心状态
3. 物理页的不可分割性
页是基本单位:操作系统和 MMU 硬件以 页(通常 4KB) 为最小单位管理物理内存。
分配粒度:即使申请 1 字节,内核也会分配整个物理页。
内存映射:虚拟地址到物理地址的映射以页为粒度建立。
4. 内核关键操作接口
内核版本:linux 6.14.8
4.1 页分配
// linux/gfp.h
// Linux 4.1 版本
struct page *alloc_pages(gfp_t gfp_mask, unsigned int order);
// Linux 6.14.8 的版本
#define alloc_pages(...) alloc_hooks(alloc_pages_noprof(__VA_ARGS__))
static inline struct page *alloc_pages_noprof(gfp_t gfp_mask, unsigned int order)
{
return alloc_pages_node_noprof(numa_node_id(), gfp_mask, order);
}
新增 alloc_pages_noprof() 函数,分离核心分配逻辑与调试/分析功能
钩子系统的引入
该函数分配 2^order 个连续的物理页面,并返回首个页面的 struct page 指针(页描述符的指针,非虚拟地址)。可以通过 page_address 转换成虚拟地址。或者直接使用下面的
__get_free_pages 函数
#define __get_free_pages(...) alloc_hooks(get_free_pages_noprof(__VA_ARGS__))
unsigned long get_free_pages_noprof(gfp_t gfp_mask, unsigned int order)
{
struct page *page;
page = alloc_pages_noprof(gfp_mask & ~__GFP_HIGHMEM, order);
if (!page)
return 0;
return (unsigned long) page_address(page); //直接返回虚拟指针
}
EXPORT_SYMBOL(get_free_pages_noprof);
如果只需要获取一个页可以直接使用
#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
4.2 获得填充为0的页
#define get_zeroed_page(...) alloc_hooks(get_zeroed_page_noprof(__VA_ARGS__))
unsigned long get_zeroed_page_noprof(gfp_t gfp_mask)
{
return get_free_pages_noprof(gfp_mask | __GFP_ZERO, 0);
}
EXPORT_SYMBOL(get_zeroed_page_noprof);
把分配的页都填充位0,保证了数据的“干净”。
4.3 释放页
void free_pages(unsigned long addr, unsigned int order);
5. 典型内存分配流程
二、区(Zone):物理内存的分区管理
1. 分区的必要性
2. 区的类型与特性
// 区类型定义(include/linux/mmzone.h)
enum zone_type {
ZONE_DMA, // 0-16MB,用于DMA设备
ZONE_DMA32, // 16MB-4GB(64位系统)
ZONE_NORMAL, // 直接映射区(32位:16MB-896MB)
ZONE_HIGHMEM, // 高端内存(仅32位需要)
ZONE_MOVABLE, // 可移动页区(反碎片)
ZONE_DEVICE, // 设备内存区(如GPU显存)
__MAX_NR_ZONES
};
3. 区的核心数据结构
struct zone {
// 水线管理
unsigned long watermark[NR_WMARK]; // 低/中/高水线
// 伙伴系统
struct free_area free_area[MAX_ORDER];
// 页回收相关
struct pglist_data *zone_pgdat; // 所属节点
unsigned long nr_active; // 活跃页计数
unsigned long nr_inactive; // 非活跃页计数
struct lruvec lruvec; // LRU链表管理
// 统计信息
atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
};
4. 水线(Watermark)管理
查看水线值示例
$ cat /proc/zoneinfo | grep -A5 Node
Node 0, zone Normal
pages free 5421
min 1482 最低警戒线,分配触发直接回收
low 1852 低警戒线,唤醒 kswapd 后台回收
high 2222 充足水位线,回收可暂停
三、页与区的协同工作原理
1. 物理内存架构全景
2. 页分配完整流程
// 简化版分配路径(mm/page_alloc.c)
struct page *alloc_pages(gfp_t gfp, unsigned order)
{
// 1. 确定允许的区类型
struct zonelist *zonelist = node_zonelist(nid, gfp);
// 2. 按区优先级遍历
for_each_zone_zonelist(zone, z, zonelist, alloc_flags) {
// 3. 检查水线
if (!zone_watermark_fast(zone, order, mark, alloc_flags))
continue;
// 4. 伙伴系统分配
page = rmqueue(zone, order, gfp_mask);
if (page)
return page;
}
// 5. 回收后重试
page = __alloc_pages_slowpath(gfp_mask, order);
}