Linux内存系统简介

Linux 内存系统是 Linux 操作系统核心功能之一,负责对物理内存(RAM)和虚拟内存进行高效管理,为进程提供独立、连续的内存抽象,并确保系统资源的合理分配与回收。

一、总体架构

二、核心组件介绍

用户空间接口层

内存分配函数

  • malloc/free
    • 功能:动态分配 / 释放用户空间内存(堆内存)。
    • 底层实现:
      • 小内存(通常 < 128KB):通过brk调整堆的边界(program break),分配连续空间,减少系统调用开销;
      • 大内存(通常 ≥ 128KB):通过mmap映射匿名内存(MAP_ANONYMOUS | MAP_PRIVATE),避免堆碎片累积;
      • 内存池:malloc内部维护内存池(如ptmallocarena结构),减少频繁分配 / 释放的开销,支持线程安全(通过互斥锁)。
    • 注意:free不会立即归还内存给内核,而是标记为空闲,供后续malloc复用(减少系统调用);若内存长期未使用,大内存块会通过munmap释放,小内存块通过brk收缩堆。
  • new/delete
    • 功能:C++ 面向对象的内存分配 / 释放,本质是malloc/free的封装,但增加了对象构造 / 析构逻辑。
    • 差异:new分配失败时抛出bad_alloc异常(或返回nullptr,取决于编译器),而malloc返回nullptrdelete[]需用于数组释放,确保每个元素的析构函数被调用。

内存映射

  • mmap
    • 核心参数:
      • addr:建议映射的起始地址(内核可调整);
      • length:映射长度(按页对齐);
      • prot:内存保护标志(PROT_READ/PROT_WRITE/PROT_EXEC);
      • flags:映射类型(MAP_SHARED:修改同步到文件;MAP_PRIVATE:私有副本,不影响文件;MAP_ANONYMOUS:匿名映射,无对应文件)。
    • 应用场景:
      • 共享库加载(如libc.so通过MAP_SHARED映射,多进程共享同一份物理内存);
      • 大文件高效读写(无需一次性加载到内存,通过页缺失按需加载);
      • 进程间共享内存(MAP_SHARED + 共享文件,或MAP_ANONYMOUS | MAP_SHARED)。
  • munmap:解除mmap创建的映射,释放虚拟地址空间,若为MAP_SHARED且有未同步修改,会刷新到文件。

堆管理

  • brk/sbrk:调整进程堆边界
    • 功能:直接调整进程 "堆的边界"(program break,堆的最高地址)。
      • brk(addr):将堆边界设置为addr(需大于当前堆起始地址);
      • sbrk(incr):将堆边界增加incr字节(incr为负则减少),返回调整前的边界地址。
    • 局限:仅能分配连续内存,频繁分配 / 释放易导致 "堆内部碎片"(已分配区域中未使用的空间);不适合大内存分配(易导致堆过度扩张)。

特殊内存

  • posix_memalign/memalign
    • 按指定字节对齐的内存(如用于硬件要求的对齐地址,如 SIMD 指令、DMA 缓冲区)。
    • 区别:posix_memalign是 POSIX 标准,返回错误码;memalign是非标准接口,失败返回NULL

系统调用层

核心系统调用

  • sys_brk:内核实现brk的核心逻辑,负责调整进程堆边界。
    • 内核处理:检查addr是否在进程虚拟地址空间范围内,更新mm_struct中的brk字段,同时建立虚拟地址到物理内存的映射(延迟分配,首次访问时触发缺页中断)。
  • sys_mmap/sys_munmap:内核实现内存映射的核心逻辑。
    • sys_mmap:在进程虚拟地址空间中分配一段连续区域,根据flags类型(文件映射 / 匿名映射)设置映射属性,并关联vm_area_struct(虚拟内存区域);
    • sys_munmap:解除指定虚拟地址范围的映射,释放vm_area_struct,若为文件映射且有脏页,会同步到磁盘。

内存保护

  • sys_mprotect:修改内存区域访问权限
    • 功能:修改虚拟内存区域的访问权限(读 / 写 / 执行),实现内存保护(如防止栈溢出、代码段不可写)。
    • 原理:通过更新对应虚拟地址范围的页表项标志位(如PTE_R/PTE_W/PTE_X),若进程访问权限不匹配,CPU 会触发 "缺页异常"(#PF),内核终止进程(如SIGSEGV信号)。
    • 应用:动态修改内存权限(如 JIT 编译器先分配可写内存,写入代码后改为只读可执行,防止篡改)。

内存统计

  • sys_mincore:查询内存页是否在物理内存中
    • 功能:查询指定虚拟地址范围内的页是否 "驻留物理内存"(in core),返回一个字节数组(每 bit 表示一个页的状态:1 = 驻留,0 = 不在内存)。
    • 用途:帮助用户空间程序优化内存使用(如预加载即将访问的页,减少缺页中断)。

交换空间

  • sys_swapon:启用 Swap 设备(分区或文件),内核将其加入 Swap 链表,用于内存页换出; + sys_swapoff:关闭 Swap 设备,需先将该设备上的所有页换入物理内存(若内存不足则失败)。

虚拟内存管理

进程地址空间

  • struct mm_struct:进程的虚拟内存实例
    • 每个进程有且仅有一个mm_struct(进程描述符task_structmm字段),是虚拟内存的 "总控结构"。
    • 核心字段:
      • pgd:页全局目录(页表的根节点);
      • mmap:虚拟内存区域链表(vm_area_struct链表);
      • brk:堆的当前边界地址;
      • total_vm:进程使用的虚拟页总数;
      • rss:驻留物理内存的页总数( Resident Set Size)。

虚拟内存区域

  • struct vm_area_stuct:虚拟地址空间被划分为多个不重叠的 VMA,每个 VMA 代表一段具有相同属性(权限、映射类型)的连续虚拟地址。
    • 核心字段:
      • vm_start/vm_end:VMA 的起始 / 结束虚拟地址;
      • vm_flags:权限标志(VM_READ/VM_WRITE/VM_EXEC)、映射类型(VM_ANON= 匿名映射,VM_FILE= 文件映射);
      • vm_ops:VMA 操作函数集(如fault:缺页处理,map_pages:批量映射页);
      • vm_file:若为文件映射,指向对应文件的struct file
    • 管理方式:内核通过 "红黑树"(mm_structmm_rb)快速查找 VMA(比链表查询效率高)。

页表管理

  • 四级页表
    • 地址转换过程:64 位虚拟地址被拆分为 6 个字段(PGD 索引 + PUD 索引 + PMD 索引 + PTE 索引 + 页内偏移),逐级查找页表,最终得到物理地址。
    • pgd:页全局目录(一级页表),每个表项指向一个 PUD;
    • pud:页上级目录(二级页表),每个表项指向一个 PMD;
    • pmd:页中间目录(三级页表),每个表项指向一个 PTE;
    • pte:页表项(四级页表),每个表项指向物理页框(或标记为 "未映射""换出" 等状态)。
  • 大页:直接使用pmd或者pgd映射2MB/1GB内存
    • 常规页大小为 4KB,大页支持 2MB(通过 PMD 直接映射)或 1GB(通过 PGD 直接映射),减少页表层级(如 2MB 大页只需 PGD→PUD→PMD 三级查找)。
    • 优势:降低 TLB miss(TLB 可缓存更多大页映射),适合内存密集型应用(如数据库、虚拟机);
    • 管理:通过/proc/sys/vm/nr_hugepages配置大页数量,用户空间通过mmapMAP_HUGETLB标志)使用。
  • 内存保护:页表项标志位:PTE_R、PTE_W、PTE_X
    • PTE_P:页存在(映射到物理页框);
    • PTE_R:可读;
    • PTE_W:可写;
    • PTE_X:可执行;
    • PTE_U:用户态可访问(否则仅内核态可访问)。

物理内存管理PMM

> 物理内存管理负责实际物理内存(RAM)的分配、回收和碎片处理,核心目标是高效利用物理页框。 >

内存域Zone

  • ZONE_DMA:低地址内存(x86 通常≤16MB),供传统 ISA 设备 DMA 访问(DMA 不经过 CPU,直接读写内存,地址需在设备可访问范围内);
  • ZONE_NORMAL:常规内存(x86 通常 16MB~896MB),内核可直接映射(虚拟地址 = 物理地址 + 固定偏移);
  • ZONE_HIGHMEM:高端内存(x86>896MB,仅 32 位系统存在),内核无法直接映射,需通过临时映射(如kmap)访问(64 位系统因地址空间充足,无此 Zone)

伙伴系统

  • 按2的n次方进行管理空闲内存 + 解决外部碎片问题,合并相邻空闲块 + 分配过程: - 若请求`n`个页框,找到最小的`2^k ≥ n`的链表; - 若链表非空,直接分配`2^k`个页框,多余部分拆分后加入小一级链表; - 若链表为空,向大一级链表申请并拆分。 + 合并过程:当页框释放时,检查相邻页框("伙伴")是否空闲,若空闲则合并为大一级页框,减少外部碎片。

页帧分配器

  • alloc_pages:分配连续物理页,分配`2^order`个连续页框(`order=0`→1 页,`order=3`→8 页); + __get_free_page:封装`alloc_pages`,返回单个页框的虚拟地址; + kmalloc:分配连续虚拟地址的小内存(基于 Slab 分配器,底层调用伙伴系统)。

特殊内存分配

CMA

预留内存,支持动态复用和页迁移

  • 作用:预留一块物理内存区域,供驱动程序(如 GPU、摄像头)分配连续大页(DMA 需求),平时可被普通进程使用(通过 "可迁移页" 标记),驱动申请时内核会迁移该区域的普通页,释放连续空间。
  • 配置:通过内核参数cma=size@start预留(如cma=256M@0x10000000)。

巨页

减少TLB压力

  • 巨页通常指 "比标准页大的页",Huge Pages 是内核预分配的静态巨页,而 "动态巨页"(Transparent Huge Pages,THP)由内核自动管理,无需用户配置,适合随机内存访问(如数据库)。

vmalloc

分配连续虚拟连续、物理离散的内存

  • 功能:分配 "虚拟地址连续、物理地址离散" 的内存(区别于kmalloc的物理连续)。
  • 用途:内核需要大内存但无需物理连续(如模块加载、网络缓冲区);
  • 缺点:因物理离散,访问需多次页表查找,性能略低于kmalloc

slab

分配内核对象(对象池)

  • 问题:伙伴系统分配的是页框级内存(4KB 起),而内核对象(如task_structinode)通常小于 4KB,直接分配会导致 "内部碎片"。
  • 原理:基于伙伴系统,为每种内核对象创建 "slab 缓存"(kmem_cache),缓存预先分配的对象(分为满 / 部分满 / 空 slab),分配时直接从缓存取对象,释放时放回缓存(不立即归还给伙伴系统)。
  • 优势:减少碎片,提高分配效率(对象复用);
  • 工具:slabtop可查看 slab 缓存使用情况(如dentry缓存、inode缓存)。

内存回收和交换

kswapd

定期检查内存使用情况,触发页回收

LRU

最近最少使用

  • 原理:将物理页分为 "活跃" 和 "不活跃" 链表(active_list/inactive_list),优先回收不活跃页。
  • 页迁移规则:
    • 首次访问的页加入inactive_list
    • 再次访问时,从inactive_list移到active_list(标记为 "活跃");
    • active_list过长,会将最久未访问的页移回inactive_list

交换空间

不活跃的页换出到磁盘

内存压缩

压缩不活跃的页

硬件抽象

MMU

实现映射

  • 功能:CPU 内置的硬件模块,负责虚拟地址到物理地址的转换(基于页表),若转换失败(如页不在内存),触发 "缺页异常"(#PF),由内核处理(换入页或终止进程)。

TLB

缓存最近使用的页表项

复制代码
- 作用:缓存最近使用的页表项(虚拟地址→物理地址映射),避免每次访问内存都查页表(页表在内存中,访问慢)。
- 刷新机制:当页表项更新(如`mprotect`修改权限),需刷新 TLB(否则 CPU 会使用旧映射),内核通过`tlb_flush`指令完成(分全局刷新和局部刷新,减少性能损耗)。

缓存一致性

  • 问题:多 CPU 或 DMA 设备访问内存时,CPU 缓存与内存数据可能不一致(如 CPU1 修改缓存,CPU2 未更新)。
  • 解决:通过硬件缓存一致性协议(如 x86 的 MESI 协议),确保所有 CPU 看到的内存数据一致;DMA 操作时,内核通过dma_sync_single_for_cpu等函数同步缓存与内存。

三、内存监控和调优工具

工具 功能 常用命令示例
free 查看总内存、已用、空闲、缓存和 Swap free -h (人性化显示)
top /htop 实时查看进程内存占用(% MEM、RES/RSS) M 键按内存占用排序
vmstat 监控内存、Swap、IO 等系统指标 vmstat 1 (每秒输出一次)
sar 历史内存使用统计(需预先配置) sar -r 1 5 (1 秒一次,共 5 次)
pmap 查看进程虚拟地址空间布局 pmap -x <PID> (详细显示进程内存映射)
slabtop 查看内核 Slab 分配器使用情况 实时显示 slab 缓存占用

四、总结

Linux 内存系统通过分层设计(用户接口→系统调用→虚拟内存→物理内存→硬件),实现了高效、安全、灵活的内存管理:

  • 虚拟内存屏蔽物理细节,提供独立地址空间;
  • 伙伴系统 + Slab 解决碎片问题,优化分配效率;
  • LRU 回收 + Swap 平衡内存利用率与性能;
  • 硬件抽象(MMU/TLB)确保地址转换高效。
相关推荐
虾..5 小时前
Linux 软硬链接和动静态库
linux·运维·服务器
Evan芙5 小时前
Linux常见的日志服务管理的常见日志服务
linux·运维·服务器
hkhkhkhkh1237 小时前
Linux设备节点基础知识
linux·服务器·驱动开发
HZero.chen8 小时前
Linux字符串处理
linux·string
张童瑶8 小时前
Linux SSH隧道代理转发及多层转发
linux·运维·ssh
汪汪队立大功1238 小时前
什么是SELinux
linux
石小千8 小时前
Linux安装OpenProject
linux·运维
柏木乃一8 小时前
进程(2)进程概念与基本操作
linux·服务器·开发语言·性能优化·shell·进程
Lime-30908 小时前
制作Ubuntu 24.04-GPU服务器测试系统盘
linux·运维·ubuntu
百年渔翁_肯肯9 小时前
Linux 与 Unix 的核心区别(清晰对比版)
linux·运维·unix