news 2026/5/2 10:05:25

【Linux从入门到精通】第42篇:深入理解Linux内存管理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Linux从入门到精通】第42篇:深入理解Linux内存管理

一、引言:内存管理,看不见的舞台

在用户空间,程序只需要malloc()就能“获得”内存,完全感觉不到背后的复杂机制。但作为运维和性能优化者,你需要看到这个看不见的舞台:

  • 为什么一个进程只用了500MB物理内存,却虚拟了4GB空间?

  • Buffer和Cache到底有什么不同?为什么重启后Cache变小了但系统反而变慢了?

  • Swap用了2GB,是不是该加内存了?

理解内存管理,是排查内存泄漏、OOM Killer、性能瓶颈的基础。今天我们逐层揭开这些谜团。

二、物理内存与虚拟内存

2.1 如果没有虚拟内存……

想象一个没有虚拟内存的系统:每个程序直接操作物理地址。程序A写了地址0x1000,程序B也写了地址0x1000——数据互相覆盖,系统瞬间崩溃。

更麻烦的是内存碎片:系统总共有2GB空闲内存,但它们是分散的几十个小块,每个只有几十MB。有一个程序需要500MB连续内存——虽然总量足够,但找不到连续的空间,分配失败。

2.2 虚拟内存的解决方案

虚拟内存为每个进程创建了一个独立、完整、连续的地址空间。在你的64位Linux系统上,每个进程都以为自己拥有128TB的虚拟地址空间(实际可用的用户空间远小于此,但也是天文数字),而实际上它可能只占用几十MB物理内存。

页表——虚拟到物理的翻译官

虚拟地址和物理地址之间的映射关系存储在一个叫页表的数据结构中。当程序访问某个虚拟地址时,CPU的MMU(内存管理单元)自动查找页表,将其翻译为物理地址:

text

虚拟地址 → [MMU查询页表] → 物理地址(可能真实存在,也可能在Swap中)

如果页表中查不到映射(比如程序访问了未分配的内存),CPU触发缺页异常(Page Fault),内核介入处理——可能是真正的非法访问(Segfault),也可能是合法但还没分配物理内存(按需分配)。

2.3 内核与用户空间的分割

进程的虚拟地址空间并非全部可以自由使用。以32位系统为例(4GB虚拟地址空间):

text

0GB ──────────────────────────────────── 4GB │ 用户空间 (3GB) │ 内核空间 (1GB) │
  • 用户空间(0-3GB):进程的代码、数据、堆、栈都在这里,每个进程独立

  • 内核空间(3-4GB):内核自身的代码和数据,所有进程共享同一份

为什么这样设计?因为当进程陷入内核执行系统调用时,页表不需要切换就能直接访问内核数据——这是一个重要的性能优化。共享内核空间的代价是用户态可用的地址范围变少,但在32位时代这个代价值得付出。

在64位系统上,用户空间和内核空间各占理论最大地址范围的一半(通常是128TB),远远超过实际需求,所以地址空间的限制在64位上可以忽略。

2.4 从free输出中看虚拟内存

bash

free -h

text

total used free shared buff/cache available Mem: 7.7G 2.3G 1.2G 450M 4.2G 4.8G Swap: 2.0G 512M 1.5G
  • total:物理内存总量

  • free完全没有被使用的物理内存

  • buff/cache:内核用来缓存文件数据的物理内存(可立即回收)

  • available应用程序实际可以申请到的内存(free + 可回收的buff/cache)

这五个指标中,available是判断“内存是否够用”的核心依据——它直接回答了“如果现在有一个新进程要分配内存,能分到多少物理内存”。

三、Buffer与Cache:同是缓存,角色不同

3.1 一句记住区别

很多系统监控把Buffer和Cache合并显示为buff/cache,让它们显得像一个东西。但实际上它们在内核中的角色截然不同:

对比维度Buffer(缓冲区)Cache(页缓存)
缓存对象文件系统的元数据(inode、目录项、超级块)文件的实际数据内容
写入操作应用程序写文件时,数据先写入Page Cache,内核标记为脏页,由后台线程异步刷入磁盘。Buffer主要参与元数据的管理层面Cache是读写路径上的核心缓存
通过/proc/meminfo查看Buffers字段Cached字段

一句话总结Cache缓存文件的内容,Buffer缓存文件的“索引”。两者在内存回收时通常被合在一起处理,所以free将它们并为一列显示。

3.2 为什么要用缓存?

因为内存比磁盘快三个数量级(纳秒 vs 毫秒)。如果一个文件已经被读过一次,内核把它缓存在内存里,下次再读就直接从内存返回,完全不碰磁盘。这就是Linux内存管理的核心哲学:空闲内存是浪费,不如拿来加速I/O

3.3 /proc/meminfo:内核的完整内存账簿

free是精简版,/proc/meminfo是完整版:

bash

cat /proc/meminfo | grep -E "^Buffers|^Cached|^SwapCached|^Dirty|^Writeback"

关键字段:

  • Buffers:就是Buffer

  • Cached:就是Cache(Page Cache)

  • SwapCached:既在Swap中又在Page Cache中的页面(换出又被读回)

  • Dirty:写缓冲中的脏页,等待写入磁盘

  • Writeback:正在写入磁盘的页

3.4 手动释放缓存:drop_caches

bash

# 查看当前缓存状态 free -h # 释放Page Cache(不清除脏页) sudo sh -c 'echo 1 > /proc/sys/vm/drop_caches' # 释放可回收的Slab对象(包括dentry和inode缓存) sudo sh -c 'echo 2 > /proc/sys/vm/drop_caches' # 释放Page Cache + Slab(最彻底) sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'

执行后的效果

bash

echo 3 | sudo tee /proc/sys/vm/drop_caches free -h # buff/cache 明显减少

重要提醒drop_caches非破坏性操作——它只释放干净的(未修改的)缓存页,脏页不会被释放。但接下来系统会变慢,因为原先缓存的热数据需要重新从磁盘读取。生产环境不要轻易执行,除非你清楚它的后果。它的正当用途是性能测试(在冷缓存条件下测量真实的磁盘I/O性能)和临时释放内存给应用程序。

四、Swap:不是备胎,是换页机制

4.1 为什么Swap用了≠内存不够?

很多人看到Swap有使用量就紧张:“是不是该加内存了?”这是一个常见的误解。

Swap的真实角色:当内存压力不大时,内核可能会主动将长时间不使用的内存页换出到Swap,腾出物理内存给活跃的进程或文件缓存使用。这是好事——把“冷数据”赶出物理内存,把宝贵的高速空间留给热数据。

什么时候应该警惕Swap?

  • 持续的换页颠簸(Thrashing):用vmstat 2观察si(Swap In)和so(Swap Out)列持续较高。这说明系统频繁在Swap和内存之间来回倒数据,性能急剧下降

  • 内存不足:当available列持续接近0时,说明确实需要更多内存

少量Swap使用(几百MB) +si/so接近0 = 正常。大量Swap持续读写 = 可能需要加内存。

4.2 换页与OOM Killer

当内存和Swap都耗尽时,内核被迫做出艰难决定:杀死一个进程来释放内存。这就是我们第17篇遇到的OOM Killer。

这个过程是:Out of Memory → 内核尝试释放缓存 → 不够 → 尝试换出页面到Swap → Swap也满了 → 调用OOM Killer选择一个“罪魁祸首”进程杀死。

dmesg可以看到OOM Killer留下的“案发现场”:

text

[123456.789] Out of memory: Killed process 12345 (mysqld) ...

4.3 swappiness:控制Swap的倾向

swappiness是一个0-100的参数,控制内核倾向于回收文件缓存还是换出匿名页

bash

cat /proc/sys/vm/swappiness # 默认60
  • 接近0:内核尽可能不换出匿名页,优先回收文件缓存。适用于希望程序数据尽可能留在物理内存的场景(如数据库服务器,不想让热数据被换出到Swap)

  • 接近100:内核积极换出匿名页,优先保留文件缓存。适用于I/O密集型场景(希望用更多内存做文件缓存加速读写)

  • 默认60:折中值,适合大多数通用场景

bash

# 临时修改 sudo sysctl vm.swappiness=10 # 永久修改 echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.conf

五、诊断工具速查

工具核心用途关键指标
free -h内存概况available是实际可用内存,buff/cache大可回收
cat /proc/meminfo内存详细统计Dirty脏页量,SwapCached缓存与换页情况
vmstat 2换页实时监控si/so持续 > 0 → 换页活动频繁,内存紧张
top/htop进程级内存RES物理内存,VIRT虚拟内存
pmap -x PID进程内存映射查看具体进程的虚拟内存分布
smem -p按比例查看PSS(比例共享大小)更准确反映真实内存占用

六、本篇小结

物理内存 vs 虚拟内存

  • 虚拟内存让每个进程拥有独立的地址空间,通过页表映射到物理内存

  • 实际上进程的虚拟地址与物理地址之间通过页表建立松散映射,物理页按需分配

Buffer vs Cache

  • Buffer:文件系统元数据的缓存(inode、目录项等)

  • Cache(Page Cache):文件内容数据的缓存

  • free -h中两者合并显示为buff/cache可以随时回收

  • 手动释放:echo 3 > /proc/sys/vm/drop_caches(非破坏性,但会降低性能)

Swap

  • Swap用了不等于内存不够——少量的Swap使用通常是内核对冷数据的正常管理

  • 警惕持续的si/so(换页颠簸)和available接近0

  • swappiness调整内核换页倾向(低值=优先保留内存给程序,高值=优先做文件I/O缓存)

动手练习

bash

# 1. 理解Buffer和Cache的数据差异 cat /proc/meminfo | grep -E "^Buffers|^Cached" # 写一个大文件触发Cache增长 dd if=/dev/zero of=/tmp/bigfile bs=1M count=100 cat /proc/meminfo | grep -E "^Buffers|^Cached" # Cached增加 rm /tmp/bigfile # 2. 体验drop_caches(仅在测试环境执行) free -h echo 3 | sudo tee /proc/sys/vm/drop_caches free -h # 观察 buff/cache 的变化 # 3. 模拟Swap使用(谨慎在测试环境操作) dd if=/dev/zero of=/tmp/bigswp bs=1M count=1000 # 创建1GB大文件 # 多运行几个同时占内存的进程,观察Swap从0变为有值 # free -h 和 vmstat 2 同时观察 # 4. 检查当前swappiness cat /proc/sys/vm/swappiness # 5. 查看进程的真实内存分布 # 找到某个进程的PID,然后 pmap -x PID | head -20

七、下篇预告

内存管理让数据在物理内存和磁盘Swap之间流转自如,但当数据真正落到磁盘上时,还有一个重要的效率优化问题:磁盘I/O调度

为什么SSD和HDD需要不同的调度策略?iostat看到的await高一定是磁盘性能瓶颈吗?下一篇我们将学习I/O调度算法与磁盘性能优化,包括CFQ、Deadline、NOOP三种调度器的适用场景,以及用ionice精细控制进程的I/O优先级。


延伸思考:你可能会问——如果buff/cache可以被回收,为什么应用程序有时还是报Out of Memory?答案在于不可回收内存。匿名页(应用程序的堆和栈)、内核的Slab分配、被mlock锁定的内存——这些都是无法回收的。available的计算公式已经排除了这些不可回收部分,所以它比free更准确地反映了“程序还能分配多少内存”。当available接近0时,系统就会启动OOM Killer。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/2 9:57:38

设计一个基于 OpenClaw 的 AI 智能体来辅助交易

下面给出一套可落地、基于 OpenClaw 的 AI 交易智能体设计,覆盖架构、角色分工、技能/记忆、风控、部署与示例流程,便于直接开发与扩展。一、设计目标与核心定位- 定位:AI 交易助手(非全自动黑盒,人在回路可控&#xf…

作者头像 李华
网站建设 2026/5/2 9:57:14

ESP32-C3 SPI实战:手把手教你驱动OLED屏幕(含完整代码)

ESP32-C3 SPI实战:手把手教你驱动OLED屏幕(含完整代码) 在嵌入式开发领域,能够快速实现硬件与软件的交互是每个开发者的核心技能。今天我们将通过一个具体案例——使用ESP32-C3的SPI接口驱动OLED屏幕,来掌握这一实用技…

作者头像 李华