典型缺页场景

几个大的缺页场景有了解过吗?

Page Fault(页面错误)是指当程序访问的虚拟内存页面不在物理内存中时发生的一种异常情况。其中,Minor fault = 不需要磁盘 I/O

场景 触发点 缺页类型 解释
访问被换出的页面【1. 换入换出】 内存紧张,kswapd 把匿名页换到 swap;进程再次访问 Major 把 swap 区里的页面重新读回内存
未加载到内存的页面【2. 冷启动】 进程刚 exec(),代码段/数据段第一次被访问 Major 磁盘→内存,把 ELF 文件读进来。页面置换算法(如FIFO、LRU)
未加载到内存的页面【3. 文件映射】 mmap() 文件后首次读写对应页 Major 文件系统→页缓存,把磁盘块搬进内存。页面置换算法(如FIFO、LRU)
延迟分配【4.共享文件页】 首次 malloc/brk 得到虚拟地址,真正写时 Minor 仅限于页已在 page cache,比如共享库页:其他进程已加载到内存,当前进程访问
延迟分配【5.匿名页按需分配】 访问到了一个“还没映射物理页”的地址 Minor 这个地址在合法的栈增长范围内或者是已扩展堆
6. 写时复制 fork() 后父子共享只读页,任一写触发 COW Minor 分配新页,复制原页,修改PTE
7. 巨型页回落(不严谨,仅参考) 申请 HugePage 失败,内核退而求其次用 4 KB 页 Minor 大页拆小页,页表重填,数据仍在内存
非法访问内存【8. 无效页面】 访问一个根本不存在的页面或非法的地址 Invalid (Major/Minor 只针对“可恢复缺页”)此种情况是段错误(Segmentation Fault)

free 命令中的buffer和cache什么情况下用到

使用free命令可以查看系统的内存使用情况。

free -h

-h选项用于以人类可读的方式显示内存大小,例如使用GB、MB等单位。

命令的输出会包含以下列:

  1. total:系统内存的总量。
  2. used:已使用的内存量。
  3. free:空闲的内存量。
  4. shared:用于共享内存的内存量。
  5. buffers:用于缓冲区的内存量。
  6. cached:用于缓存的内存量。
  7. available:可用的内存量。

available 是系统目前可供使用的最大内存,free是指直接可用的内存。他们的差值来源于buff/cache(虽然是已分配给缓存和缓冲区的)系统有需要的话可以拿出一部分内存给进程用。

下面是一个示例输出:

              total        used        free      shared  buff/cache   available
Mem:           7.7G        3.2G        1.5G        500M        3.0G        3.8G
Swap:          2.0G        1.2G        819M

在这个示例中,系统的总内存为7.7GB,已使用的内存为3.2GB,空闲的内存为1.5GB。同时,有500MB的内存用于共享,3.0GB的内存用于缓存,还有3.8GB的内存可用。

“cache”部分表示操作系统所使用的文件系统缓存,也就是磁盘上的文件数据的副本,以提高读取文件的性能。这个缓存会根据系统的需求动态地调整大小。通常,较大的cache值表示系统正在积极地使用内存来缓存文件数据,以提高读写文件的速度。buffer cache,主要用于存放块设备的缓冲数据。


内存回收

Linux内核为每个内存区域(zone)定义了三个水位(单位是页面数):

  • High (high watermark):理想空闲内存量,异步回收的目标水位。(free_pages(伙伴系统的链表) < highkswapd启动。)
  • Low (low watermark):直接回收的触发阈值,低于此值会同步回收。
  • Min (min watermark):极端内存压力下的阈值,可能触发OOM Killer。

1. 直接内存回收(Direct Reclaim)

系统发现空闲内存低于最低水位(min watermark),且无法通过其他途径(如缓存释放)获得足够内存,内核会同步阻塞当前进程,立即执行内存回收,直到释放足够内存供分配。

回收目标:包括页面缓存(Page Cache)、slab缓存、匿名页(可能触发交换SWAP)。

2. 异步内存回收(Background Reclaim)

由内核线程kswapd在后台定期检查内存水位,若空闲内存低于高水位(high watermark)但高于最低水位(min)kswapd异步回收内存,提前释放页面。

回收策略:优先回收干净页面(如未修改的Page Cache),减少I/O压力。

OOM Killer 的选择并非随机,而是基于一个评分算法。计算 oom_score 的核心思想是:在内存紧张的情况下,杀死一个进程能释放大量内存,同时造成的系统损失(代价)又最小。(父进程、用户级进程、运行时间很长的进程例如数据库服务、守护进程的分数更高)


内存碎片有哪些

类型 定义 产生原因 影响 常见场景 解决办法
外部碎片 空闲区不连续 多次分配与释放 无法分配大块连续空间 堆分配、OS内核 压缩、分页、伙伴系统
内部碎片 已分配的内存块中,未被实际使用的部分 固定块、对齐 内存浪费 malloc/slab分配器 减小粒度、分级分配
页碎片 页内部碎片:一个页未被完全利用。 页表碎片:页表项分散或页帧映射不连续。 页管理机制 性能下降 虚拟内存、数据库 大页、NUMA优化
文件碎片 文件块不连续 文件频繁修改 I/O变慢 文件系统 磁盘整理、碎片整理

内存分配

1. 伙伴系统

伙伴系统一次能够分配的最大连续内存块为 $ 2^{10} $ 个页面,即 1024 个页面,4MB。
THP 的分配对应于 order 为 9 的内存,也从伙伴系统分配。

2. 缺页异常

Minor Fault(次要缺页)

  • 当虚拟页没有映射到进程的页表上,但它实际上已经在物理内存中(比如共享页或已经在内存缓存中)时发生。
  • 处理这类缺页异常不需要从磁盘读取数据,只需要更新页表即可。

Major Fault(主要缺页)

  • 当虚拟页不在物理内存中,需要从 磁盘(swap 或文件) 读取到内存中才能继续执行时发生。

触发条件

  • CPU 尝试访问虚拟地址对应的页,但 页表中没有有效映射
  • 并且该页 不在物理内存中(或者被换出到 swap 区)。
  • 系统捕获到缺页异常 → 进入内核处理。

处理过程

  • 处理器将当前执行状态保存到内核栈。
  • 跳转到操作系统的缺页异常处理程序(page fault handler)。
  • 检查缺页地址:是否属于有效的虚拟内存区域(进程已经分配了虚拟内存的地址范围,或者操作系统可以为其分配(如堆或栈可按需扩展)),如果访问非法地址 → Segmentation fault
  • OS 查找空闲物理页:如果内存充足,直接分配(选择内存区域(zone)+伙伴系统free area)。如果内存不足 → 触发 页替换算法(如 LRU、Clock),选择某页换出到 swap 区释放空间。
  • 写入页内容:文件页从可执行文件或共享库文件中读取到内存;匿名页如果第一次访问,通常初始化为全零页;如果之前被换出,则从swap 区读取回内存。major fault 的核心开销,涉及 磁盘 I/O,通常耗时几毫秒。minor fault也会到内核态,但是物理内存已经有想要的页面了,直接改页表就好了。
  • 更新页表:将新分配的物理页与虚拟地址映射到页表中,刷新CPU TLB
  • 返回用户态:恢复 CPU 状态,重新执行引发缺页的指令。

文章作者: 易百分
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 易百分 !
  目录