Linux 虚拟内存与页缓存

举报
yd_231768394 发表于 2026/11/05 20:04:03 2026/11/05
【摘要】 Linux 虚拟内存与页缓存本文讲清 Linux 内存管理的核心机制:虚拟地址如何映射到物理页、缺页与按需调页、页缓存如何加速 IO,以及 free 看到的数字到底是什么意思。 为什么需要虚拟内存每个进程都以为自己独占一整块连续地址空间,实际物理内存由内核统一分配。虚拟内存带来三个好处:进程间隔离、可以分配比物理内存更大的地址空间、内存可以按页懒加载。32 位进程地址空间约 4GB,64 ...

Linux 虚拟内存与页缓存

本文讲清 Linux 内存管理的核心机制:虚拟地址如何映射到物理页、缺页与按需调页、页缓存如何加速 IO,以及 free 看到的数字到底是什么意思。

为什么需要虚拟内存

每个进程都以为自己独占一整块连续地址空间,实际物理内存由内核统一分配。虚拟内存带来三个好处:进程间隔离、可以分配比物理内存更大的地址空间、内存可以按页懒加载。

32 位进程地址空间约 4GB,64 位则是 128TB 级别,实际可用受物理内存和交换空间限制。

地址翻译:页表与 TLB

虚拟地址被切成页号和页内偏移,页号查页表得到物理页框号,再拼上偏移。为了节省页表本身的内存,Linux 用多级页表(x86-64 通常是四级:PGD → PUD → PMD → PTE)。

页表查询很慢,所以 CPU 里有 TLB 缓存最近用过的映射。TLB 命中就跳过页表;未命中才走多级查询。进程切换会刷新 TLB(PCID 可缓解),这也是上下文切换成本的一部分。

大页(HugePage)的作用就是减少页表层级和 TLB 压力:2MB 或 1GB 一页,同样内存只需更少的表项。

缺页与按需调页

malloc 只分配虚拟地址,并不立刻给物理页。第一次访问时才触发缺页异常,内核分配物理页并建立映射,这叫按需调页(demand paging)。缺页分几类:

  • 匿名页缺页:堆、栈首次访问,分配新物理页。
  • 文件页缺页:访问 mmap 的文件或可执行文件,从页缓存映射。
  • 写时复制(COW):fork 后父子共享页,任一方写入时才复制。

缺页是正常的,但频繁的 major fault(需要读磁盘)会明显拖慢程序。

页缓存:读写的真相

Linux 把空闲内存几乎全部用作页缓存(page cache)。读文件时,数据先进入页缓存,下次命中就直接从内存返回;写文件默认是写缓存(write-back),先改页缓存并标记为脏页,由内核线程周期性回写磁盘。

这也解释了一个常见疑惑:free -h 里 buff/cache 很大、free 很小,并不代表内存不够,只是内核把没用到的内存拿去缓存了。真正要看的是 available。

free -h
#               total        used        free      shared  buff/cache   available
# Mem:           31Gi        12Gi       1.2Gi       300Mi        18Gi        18Gi
# available 才是应用还能拿到的量

mmap 与文件 IO

mmap 把文件映射进地址空间,读写内存就像读写文件,省掉一次用户态拷贝。适合大文件随机访问、多个进程共享同一份数据。

int fd = open("data.bin", O_RDONLY);
void *p = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
// 直接按数组访问 p,缺页时内核从页缓存/磁盘加载
munmap(p, size);
close(fd);

注意:MAP_PRIVATE 的写入是 COW,不会落盘;MAP_SHARED 才会同步回文件。用完必须 munmap,否则地址空间泄漏。

内存回收与 OOM

物理内存紧张时,内核先回收可回收页:干净的文件页直接丢弃(有磁盘副本),脏页先回写再丢弃,匿名页则写入 swap(如果开了)。

回收不及就会触发 OOM Killer,按 oom_score 选一个进程杀掉。容器场景里,cgroup 内存超限会先触发 cgroup 级 OOM,只杀容器内进程。

# 查看内存与回收情况
cat /proc/meminfo
vmstat 1
# 查看某个进程的 OOM 评分
cat /proc/<pid>/oom_score

常见指标怎么读

指标 含义 关注点
available 应用可用内存估算 真正的余量
buff/cache 页缓存 + 缓冲区 大是好事,可回收
si/so(vmstat) swap 换入/换出速率 持续非零说明内存压力大
pgmajfault 主缺页次数 高说明频繁读磁盘
dirty 待回写脏页 过高可能造成写抖动

实战:内存问题的排查顺序

  1. 先看 free -h 的 available,确认是不是真的不足。
  2. ps aux --sort=-rss 找占用最高的进程。
  3. cat /proc/<pid>/status 看 VmRSS、VmSwap。
  4. smem -tk 看 PSS,避免共享库重复计算。
  5. vmstat 1 看 si/so 和 wa,判断是否在换页或等 IO。
  6. 容器里看 cgroup:cat /sys/fs/cgroup/memory.max 与 memory.current。

最佳实践

  1. 不要看到 free 低就加内存,先看 available 和 swap 活动。
  2. 大文件顺序读写用普通 read/write 即可,页缓存已经很好;随机访问再考虑 mmap。
  3. 数据库类应用可调 vm.dirty_ratio、vm.dirty_background_ratio 控制回写节奏。
  4. 容器要设置合理的内存 limit,并给 JVM/Go 运行时配置匹配的堆上限。
  5. 关注 major fault 和 swap,它们比内存使用率更能反映真实压力。
  6. 大页适合数据库、虚拟化等 TLB 压力大的场景,但要预分配并评估碎片。

总结

机制 作用
虚拟地址 + 多级页表 进程隔离、按需分配
TLB / 大页 加速地址翻译
缺页 懒加载、COW、文件映射
页缓存 读写都先过内存,回写磁盘
回收 + OOM 内存紧张时的兜底

Linux 内存管理的核心思路是"把空闲内存尽量用起来,同时保证必要时能收回来"。理解页缓存和 available,就不会再被 free 的数字吓到。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。