详细介绍Linux 内存管理 匿名页面和page cache页面有什么区别?

发布于:2025-08-30 ⋅ 阅读:(17) ⋅ 点赞:(0)

Linux 内存管理:匿名页面 vs Page Cache 页面深度解析

 

在 Linux 内存管理中,匿名页面 (Anonymous Pages) 和 页面缓存 (Page Cache) 是两种核心内存类型,它们在物理内存中存储不同类型的数据,并由不同的内核机制管理。下面从 5 个维度进行详细对比:

 

1. 核心概念对比

 

特性 匿名页面 页面缓存

 

数据来源 用户进程动态内存分配 文件系统内容缓存

 

典型内容 堆、栈、共享内存、mmap(MAP_ANON) 文件内容、目录、块设备数据

 

存储位置 RAM RAM (文件数据的缓存副本)

 

生命周期 与进程共存 可长期存在(直到缓存淘汰)

 

后备存储 交换空间 (Swap) 磁盘文件

 

管理对象 struct anon_vma struct address_space

 

页表项标志 VM_SHARED 和 VM_PRIVATE 无特定标志

 

2. 使用场景对比

 

匿名页面场景

 

// 动态内存分配

void *heap_mem = malloc(1024); // glibc底层使用brk/sbrk

 

// 栈内存

int stack_var = 42;

 

// mmap匿名映射

void *anon_mem = mmap(NULL, 8192, 

                      PROT_READ|PROT_WRITE, 

                      MAP_PRIVATE|MAP_ANONYMOUS, 

                      -1, 0);

       

 

页面缓存场景

 

// 文件读写

int fd = open("data.txt", O_RDWR);

char buf[4096];

read(fd, buf, 4096); // 数据加载到page cache

 

// mmap文件映射

void *file_mem = mmap(NULL, 8192, 

                      PROT_READ|PROT_WRITE, 

                      MAP_SHARED, 

                      fd, 0);

 

3. 内核数据结构对比

 

匿名页面反向映射       

 

struct anon_vma {

    struct rw_semaphore rwsem; // 读写锁

    atomic_t refcount; // 引用计数

    struct anon_vma_chain *root;

};

 

页面缓存组织

 

struct address_space {

    struct inode *host; // 所属inode

    struct xarray i_pages; // 页缓存树

    struct rw_semaphore i_mmap_rwsem;

    unsigned long nrpages; // 缓存页数

};

 

4. 缺页异常处理流程对比

 

匿名页面缺页处理 (do_anonymous_page)

 

触发缺页

 

是否为写访问?

 

-->分配新物理页-->初始化页面内容为0-->建立页表映射

 

-->映射零页

 

页面缓存缺页处理 (filemap_fault)

 

触发缺页

 

页面是否在缓存中?

 

-->映射缓存页

 

-->预读策略-->从磁盘读取数据-->创建新缓存页-->建立页表映射

 

5. 回收机制对比

 

匿名页面回收

 

通过 kswapd 扫描 LRU_INACTIVE_ANON 列表

 

使用交换子系统的反向映射 (try_to_unmap)

 

写入交换空间后释放物理页

 

页面缓存回收

 

通过 kswapd 扫描 LRU_INACTIVE_FILE 列表

 

干净页面直接释放

 

脏页触发回写机制 (writepages)

 

实战代码分析:两种页面行为对比

 

#include <stdio.h>

#include <stdlib.h>

#include <sys/mman.h>

#include <fcntl.h>

#include <unistd.h>

 

#define SIZE (2 * 1024 * 1024) // 2MB

 

int main() {

    // 场景1: 匿名页面操作

    char *anon_mem = mmap(NULL, SIZE, 

                         PROT_READ | PROT_WRITE,

                         MAP_PRIVATE | MAP_ANONYMOUS, 

                         -1, 0);

    

    // 写入数据(触发物理页分配)

    for (int i = 0; i < SIZE; i += 4096) 

        anon_mem[i] = 'A';

    

    printf("匿名页已分配\n");

    

    // 场景2: Page Cache操作

    int fd = open("test.dat", O_RDWR | O_CREAT, 0666);

    ftruncate(fd, SIZE); // 创建2MB文件

    

    char *file_mem = mmap(NULL, SIZE, 

                         PROT_READ | PROT_WRITE,

                         MAP_SHARED, 

                         fd, 0);

    

    // 写入数据(进入page cache)

    for (int i = 0; i < SIZE; i += 4096)

        file_mem[i] = 'B';

    

    printf("Page Cache已填充\n");

    msync(file_mem, SIZE, MS_SYNC); // 强制回写磁盘

    

    // 查看内存统计

    system("grep -E 'AnonPages|PageCache' /proc/meminfo");

    

    // 释放资源

    munmap(anon_mem, SIZE);

    munmap(file_mem, SIZE);

    close(fd);

    

    // 查看回收后状态

    system("grep -E 'AnonPages|PageCache' /proc/meminfo");

    

    return 0;

}

 

输出分析:

 

匿名页已分配

Page Cache已填充

AnonPages: 2048 kB # 匿名页占用

PageCache: 2048 kB # 页面缓存占用

 

# 释放后

AnonPages: 0 kB # 立即归零

PageCache: 16 kB # 元数据残留

 

6. 高级行为对比

 

行为 匿名页面 页面缓存

 

预读机制 无 文件系统级预读 (readahead)

 

透明大页(THP) 支持 (MADV_HUGEPAGE) 部分支持(依赖文件系统)

 

内存回收优先级 高 (vm.swappiness控制) 低 (vm.vfs_cache_pressure控制)

 

脏页处理 必须换出到swap 可延迟回写到文件系统

 

共享机制 写时复制 (COW) 直接共享

 

7. 性能优化技巧

 

匿名页面优化

 

// 1. 使用大页

madvise(anon_mem, SIZE, MADV_HUGEPAGE);

 

// 2. 禁用交换

mlock(anon_mem, SIZE); // 防止被换出

 

页面缓存优化

 

// 1. 预读设置

readahead(fd, 0, 4 * 4096); // 预读4个页面

 

// 2. 绕过缓存

int fd = open("file.dat", O_DIRECT); // 直接IO

 

 

总结:核心差异表

 

维度 匿名页面 页面缓存

 

数据来源 进程动态创建 存储设备文件

 

内核管理 anon_vma链 + swap address_space树 + 回写机制

 

缺页成本 低(零页或新分配) 高(可能触发磁盘IO)

 

生命周期 绑定进程生命周期 与文件缓存策略相关

 

共享开销 高(COW复制) 低(直接共享)

 

回收影响 强制swap-out(性能损失大) 可能仅丢失缓存(性能损失小)

 

理解这些差异对于开发高性能应用和诊断内存问题至关重要。通过合理选择内存类型,可以显著优化程序性能并减少系统开销。


网站公告

今日签到

点亮在社区的每一天
去签到