当前位置: 首页 > news >正文

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

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(性能损失大) 可能仅丢失缓存(性能损失小)

 

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

http://www.xdnf.cn/news/1382977.html

相关文章:

  • SplinePSF——应用于光学成像中的 PSF 建模
  • 详细介绍Linux 内存管理struct page数据结构中的_count和_mapcount有什么区别?
  • 图论好题推荐-逛公园
  • 【LInux】常用命令笔记
  • ArkUI框架之Canvas 画布
  • 什么是最小二乘法
  • 二、开关电源的EMC改善措施
  • CVPR2025丨VL2Lite:如何将巨型VLM的“知识”精炼后灌入轻量网络?这项蒸馏技术实现了任务专用的极致压缩
  • 虚幻基础:角色变换角色视角蒙太奇运动
  • 基于SpringBoot的老年人健康数据远程监控管理系统【2026最新】
  • 嵌入式开发学习———Qt软件环境下的C++学习(七)
  • 图论基础篇
  • Mybatis中缓存机制的理解以及优缺点
  • 微服务相关面试题
  • stable-baseline3介绍
  • 个人博客运行3个月记录
  • mac m4执行nvm install 14.19.1报错,安装低版本node报错解决
  • 【STM32】G030单片机的窗口看门狗
  • Flutter:ios打包ipa,证书申请,Xcode打包,完整流程
  • LeetCode Hot 100 第7天
  • mac系统本地部署Dify步骤梳理
  • 仓颉编程语言青少年基础教程:输入输出
  • 模拟实现Linux中的进度条
  • [Mysql数据库] 知识点总结5
  • 天津医科大学肿瘤医院冷热源群控系统调试完成:以 “精准控温 + 高效节能” 守护医疗核心场景
  • 实战演练(一):从零构建一个功能完备的Todo List应用
  • Spring事务管理机制深度解析:从JDBC基础到Spring高级实现
  • 力扣(LeetCode) ——965. 单值二叉树(C语言)
  • C#写的一键自动测灯带的应用 AI帮写的。
  • [灵动微电子 MM32BIN560CN MM32SPIN0280]读懂电机MCU之串口DMA