Linux之内存管理
一、为什么需要内存管理?
1. 没有内存管理时,系统面临的困境
早期无内存管理的嵌入式系统(裸机/极简系统),直接操作物理内存,存在三大致命问题:
-
地址无隔离:多个进程/任务共用物理内存,一个程序越界写入会直接破坏其他程序数据,导致系统崩溃(嵌入式场景中常表现为固件卡死、设备异常重启)。
-
内存碎片严重:频繁分配/释放不同大小的内存块,会产生大量无法利用的碎片,即使总空闲内存充足,也无法分配大块连续内存(如DMA缓冲区需求)。
-
资源利用率极低:嵌入式内存多为MB级,无缓存机制时,I/O操作直接读写磁盘/Flash,速度慢且浪费内存;无虚拟内存时,进程大小受物理内存严格限制。

2. 内存管理的核心价值:解决痛点,支撑稳定运行
Linux内存管理模块的核心目标,就是针对性解决上述问题,实现"有限内存最大化利用+系统稳定可靠":
-
通过虚拟内存实现进程地址隔离,避免越界干扰;
-
通过伙伴系统、Slab分配器解决内存碎片与高效分配问题;
-
通过Cache/Buffer缓存提升I/O效率,通过Swap扩展内存可用空间。

总结:内存管理是Linux系统的"内存管家",核心是在有限资源下平衡隔离性、效率与稳定性。
二、核心脉络
| 核心问题 | 解决方案 | 实际效果 | 适配要点 |
|---|---|---|---|
| 地址隔离与进程独立空间 | 虚拟内存+MMU+页表映射(分段/分页/段页式) | 进程互不干扰,支持多任务稳定运行 | 无MMU系统需放弃虚拟内存,靠代码规范避免越界 |
| 大块连续物理内存分配与碎片 | Buddy System(伙伴系统) | 高效分配连续页框,回收时合并碎片 | 适配DMA缓冲区需求 |
| 小块内核对象分配低效 | Slab分配器(对象缓存池) | 复用内存,减少碎片,提升小对象分配效率 | 适配驱动频繁创建/销毁结构体的场景 |
| I/O操作速度慢,浪费内存 | Buffer(块设备缓存)+ Cache(文件缓存) | 减少磁盘/Flash读写,提升I/O效率 | 内存紧张时可回收,大文件读写可跳过Cache |
| 物理内存耗尽导致系统崩溃 | Swap机制+OOM Killer+内存回收(kswapd) | 临时扩展内存,选择性杀进程保系统 | 实时场景禁用Swap,核心进程设OOM保护 |
| 多CPU内存访问性能瓶颈 | NUMA架构+节点内存分配策略 | 优先访问本地内存,降低跨节点延迟 | 配置zone_reclaim_mode=0,优先跨节点取内存 |

三、核心概念
1. 虚拟内存底层实现:分段、分页与段页式
虚拟内存的核心是地址映射,Linux结合Intel架构特性,采用"段式映射打底、页式映射为主"的方案,解决地址隔离与碎片问题。
1.1 内存分段:按逻辑划分,解决隔离与权限控制
将程序按逻辑划分为代码段、数据段、堆段、栈段,每个段有独立基地址、界限和权限(如代码段只读)。虚拟地址由"段选择因子+段内偏移"组成,通过段表映射到物理地址。

优缺点:无内部碎片,但易产生外部碎片(段大小不固定),内存交换效率低(需整体换出整个段)。
1.2 内存分页:按固定大小划分,解决碎片问题
将虚拟内存与物理内存均划分为4KB固定大小的页,虚拟地址拆分为"页号+页内偏移",通过页表映射物理页。Linux采用多级页表(32位二级、64位四级),减少页表内存占用。

核心机制:
-
缺页异常:访问未映射虚拟页时触发,内核分配物理页并更新页表,主缺页(需读磁盘)耗时远高于次缺页。
-
TLB快表 :CPU内置的页表缓存,加速虚拟地址到物理地址转换,命中率极高(依赖程序局部性原理),CPU 在寻址时,会先查TLB,如果没找到,才会继续查常规的页表。

优缺点:无外部碎片,内存交换效率高(仅换出不常用页),但存在内部碎片(程序不足一页也占整页),是Linux内存管理的核心基础。
1.3 段页式:结合两者优势,兼顾逻辑与效率
先分段(按逻辑隔离),再对每个段分页(按固定大小解决碎片),地址结构为"段号+段内页号+页内偏移"。需两次查表(段表→页表),Linux因Intel架构兼容保留该机制,但实际屏蔽段映射作用(所有段基地址为0),仅用段做权限控制。

2. 用户空间 vs 内核空间:地址划分与访问规则
-
32位划分:0 ~ 3GB用户空间(进程私有,含代码段、数据段、BSS段、堆、栈、文件映射区),3~4GB内核空间(全局共享,含直接映射区、vmalloc区、高端内存)。
-
64位划分 :低128TB为用户空间,高128TB为内核空间,中间为未定义区域,适配大内存场景。

-
访问规则:用户态需通过系统调用(如brk/mmap)切换到内核态,才能访问内核空间;内核空间可直接访问所有物理内存(受权限限制)。
虽然每个进程都各自有独立的虚拟内存,但是每个虚拟内存中的内核地址,其实关联的都是相同的物理内存。这样,进程切换到内核态后,就可以很方便地访问内核空间内存。

用户空间分布情况如下:

3. 物理内存管理:内存模型与NUMA架构
3.1 三种物理内存模型
内核按物理内存布局差异,提供三种管理模型:
-
FLATMEM(平坦内存模型):适用于连续物理内存,用全局数组mem_map管理所有页,计算简单高效,是小内存平台默认选择。
-
DISCONTIGMEM(非连续内存模型):将不连续内存划分为多个节点(Node),每个节点内部连续,避免为内存空洞分配页结构,适配存在内存空洞的硬件。
-
SPARSEMEM(稀疏内存模型):支持内存热插拔,将内存划分为更小的段(Section),每个段可独立上线/下线,适用于高端服务器。
3.2 NUMA架构:多CPU内存访问优化
传统UMA架构(所有CPU共享总线访问内存)随CPU核数增加易出现总线瓶颈,NUMA架构将CPU与本地内存划分为节点,优先访问本地内存,跨节点访问需通过QPI总线,延迟更高。

适配要点:
-
通过
numactl -H查看节点信息,配置zone_reclaim_mode=0,本地内存不足时优先访问其他节点内存,避免频繁回收。 -
通过set_mempolicy接口设置内存分配策略(如MPOL_LOCAL优先本地节点),适配实时场景低延迟需求。
4. 内存回收与OOM:系统稳定性核心保障
4.1 可回收内存类型
-
文件页:缓存的磁盘文件/块设备数据,干净页可直接释放,脏页需写回磁盘后释放,回收优先级高。
-
匿名页:堆、栈等无载体内存,需通过Swap换出到磁盘。
4.2 回收机制
-
后台回收(kswapd):异步回收,不阻塞进程,当内存低于低水位线(pages_low)时唤醒,需合理配置min_free_kbytes,尽早触发后台回收。
-
直接回收:后台回收不足时同步回收,阻塞进程,导致系统卡顿,需通过优化减少直接回收频率。
4.3 OOM Killer机制
内存耗尽时,内核通过oom_badness函数给进程打分(内存占用+oom_score_adj),得分最高者被杀死。需将sshd、核心业务进程的oom_score_adj设为-1000,避免被查杀。
总结:缺页是"补内存映射",Swap是"借磁盘空间",OOM是"丢车保帅",内存回收是"动态腾空间",四者共同保障内存不足时系统可控。
4.4 内存回收流程

四、 工具:快速判断内存状态
1. free命令(核心看available)
执行free -h,重点关注:
-
free:完全空闲内存;
-
buff/cache:可回收缓存总和;
-
available:真实可用内存(free+可回收缓存),判断内存是否充足的核心指标。
2. top/vmstat(看压力与瓶颈)
-
top:按M键排序,关注%MEM(内存占比)、RES(常驻物理内存);
-
vmstat:关注si/so(Swap换入/换出,非零即异常)、majflt(主缺页,过高有I/O瓶颈)。
3. 进阶工具:定位碎片与回收问题
-
cat /proc/buddyinfo:查看伙伴系统空闲页块分布,判断外部碎片(小块页多、大块页少即为碎片严重)。 -
sar -B 1:监控内存回收指标,pgscank(后台回收页)、pgscand(直接回收页)、pgsteal(回收成功页),pgscand过高需优化。 -
cat /proc/meminfo:查看Active/Inactive页、Swap使用情况,辅助判断内存回收倾向。
总结:free看可用,top看进程,vmstat看趋势,进阶工具查根源,组合使用可快速定位各类内存问题。
五、核心表
表1:内存分配函数对比(kmalloc vs vmalloc vs malloc)
| 特性 | kmalloc | vmalloc | malloc |
|---|---|---|---|
| 分配区域 | 内核空间,物理连续 | 内核空间,物理不连续 | 用户空间,虚拟连续 |
| 物理连续性 | 是 | 否(虚拟连续) | 不保证 |
| 适用场景 | 驱动缓冲区、内核结构体 | 内核大内存,无DMA需求 | 用户态应用堆内存 |
| 底层依赖 | 伙伴系统 | 页表映射(物理页不连续) | brk/mmap系统调用 |
表2:Buffer vs Cache对比
| 特性 | Buffer(缓冲区) | Cache(页缓存) |
|---|---|---|
| 管理对象 | 块设备(磁盘/Flash)扇区 | 文件系统页数据 |
| 核心作用 | 缓存块设备I/O数据 | 缓存文件读写数据,提升效率 |
| 回收优先级 | 低 | 高 |
表3:内存模型对比
| 模型 | 适用场景 | 优势 | 适配建议 |
|---|---|---|---|
| FLATMEM | 连续物理内存,小内存平台 | 计算简单,占用内存少 | 默认选择,适配多数MCU/SoC |
| DISCONTIGMEM | 存在内存空洞的硬件 | 避免空洞浪费页结构 | 按需开启,需硬件支持 |
| SPARSEMEM | 支持内存热插拔的高端平台 | 灵活性高,支持动态扩展 | 服务器场景使用 |
六、内存优化
-
Swappiness优化 :实时场景设
vm.swappiness=0禁用Swap,非实时设5~10,避免影响速度。 -
HugePages配置:大内存应用启用,减少页表开销与缺页异常,注意预留内存。
-
进程保护与限制 :核心进程设
oom_score_adj=-1000,用cgroup限制进程内存上限。 -
缓存优化 :大文件一次性读写用
O_DIRECT跳过Cache,复用内存池减少小对象碎片。 -
内存回收参数调优 :增大
min_free_kbytes尽早触发后台回收,减少直接回收;调整watermark_scale_factor优化水位线间距,适配内存大小。 -
NUMA架构优化 :多CPU平台设
zone_reclaim_mode=0,优先跨节点分配内存;通过numactl绑定进程与CPU节点,降低跨节点访问延迟。 -
页表优化:开启多级页表压缩,减少页表内存占用;小内存平台可适当增大页大小(如8KB/16KB),降低缺页频率。
七、常见问题
基础概念类
- Linux系统中,虚拟内存的作用是什么?无MMU的系统如何管理内存?
答:虚拟内存实现进程地址隔离与内存复用,允许进程使用超过物理内存的空间;无MMU系统无虚拟内存,需直接操作物理内存,靠代码规范(如内存边界检查)避免越界,用内存池复用内存块,减少碎片。
- Buddy系统与Slab分配器的核心区别及适用场景?
答:Buddy系统按2的幂次方管理连续物理页,适配大块内存分配(如DMA缓冲区),解决外部碎片;Slab分配器缓存内核小对象(如task_struct),减少小块内存分配开销与内部碎片。驱动优先用Buddy分配连续内存,Slab优化结构体频繁创建/销毁场景。
- Linux为什么采用"段式+页式"混合映射,而非单独用分页?
答:因Intel X86架构硬件强制先进行段式映射,Linux为兼容硬件只能保留段机制;但Linux将所有段基地址设为0,屏蔽段的地址转换作用,仅用段做权限控制(如代码段只读),实际地址转换依赖分页,兼顾兼容性与效率。
问题排查类
-
系统明明有空闲内存,为什么进程被OOM Killer杀掉?
答:空闲内存是free字段,真实可用内存是available(free+可回收缓存);若available过低,且缓存无法回收(如脏页过多、无Swap的匿名页),内核会触发OOM。需通过sar -B查看回收情况,优化缓存回收或增大可用内存。
-
如何判断系统存在内存碎片问题?如何优化?
答:通过
cat /proc/buddyinfo查看,若小块页(如20、21页框)数量多,大块页(如2^10以上)数量少,即为外部碎片严重。优化方式:用Slab分配器管理小对象、复用内存池、避免频繁分配不同大小内存块、开启内存规整机制。 -
系统频繁出现直接内存回收(pgscand过高),如何解决?
答:增大
min_free_kbytes,提高内存低水位线,尽早唤醒kswapd后台回收;调整swappiness倾向回收文件页,减少匿名页回收压力;优化应用程序,减少内存泄漏与频繁内存申请,避免内存快速耗尽。
实战优化类
-
实时系统中,内存管理需要注意哪些点?
答:禁用Swap保实时性,避免换入换出延迟;核心进程设OOM保护,防止被查杀;优先用kmalloc分配连续物理内存,适配DMA需求;优化min_free_kbytes,减少直接内存回收阻塞;关闭不必要的缓存(如大文件用O_DIRECT),减少I/O耗时;避免主缺页,提前加载关键数据到内存。
-
kmalloc和vmalloc的选择依据是什么?嵌入式驱动中优先用哪个?
答:选择依据是是否需要物理连续内存。需物理连续(如DMA缓冲区、硬件I/O交互)用kmalloc;大内存且无连续需求(如内核缓冲区、日志存储)用vmalloc。嵌入式驱动优先用kmalloc,因多数硬件I/O要求连续物理地址,vmalloc存在地址转换开销,且可能影响实时性。
-
NUMA架构下,系统出现局部内存不足但整体内存充足,如何解决?
答:将
/proc/sys/vm/zone_reclaim_mode设为0,允许本地内存不足时从其他节点分配内存,避免本地频繁回收;通过numactl绑定进程到内存充足的节点,优化内存访问路径;调整内存分配策略为MPOL_PREFERRED,优先使用指定节点内存,平衡各节点内存负载。
八、总结
Linux内存管理的核心是"在有限内存中,平衡隔离性、效率与实时性":
-
核心脉络:从"无内存管理的三大痛点"出发,通过虚拟内存(分段/分页)、伙伴系统、Slab、缓存、内存回收等机制逐一解决,形成完整技术闭环,同时适配硬件特性(如无MMU、小内存、NUMA架构)。
-
重点:分配函数对比、OOM触发逻辑、Swap与缓存优化、无MMU系统适配、NUMA架构调优、内存碎片排查与解决。
-
原则:优先保证实时性与稳定性,保护核心进程,减少碎片与不必要的I/O开销,根据硬件配置(内存大小、CPU架构、是否有MMU)灵活调整参数与策略。