1. 基础 - 页和区

一、页(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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值