Linux内存管理

Linux负责把有限的物理内存(RAM)抽象成大量连续的虚拟地址空间,并通过页表、分页、缓存、回收和交换机制,在进程之间安全、高效地分配和复用内存。


1.内存管理逻辑图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
             进程
│
┌───────────┴───────────┐
│ 虚拟地址空间 │
│ Code / Data / Heap │
│ mmap / Stack │
└───────────┬───────────┘
│
页表
│
虚拟页 → 物理页
│
┌───────────┴───────────┐
│ 物理内存 │
│ RAM │
│ Page / Page Frame │
└───────────┬───────────┘
│
┌───────────┴───────────┐
│ │
Page Cache Swap
│ │
文件系统 磁盘

2.虚拟内存

假设机器有:

1
物理内存:16GB

一个进程看到的却不是简单的16GB物理内存,而是一个虚拟地址空间。

CPU访问这个地址时,大致经过:

1
2
3
4
5
6
7
8
9
虚拟地址
↓
MMU
↓
TLB / Page Table
↓
物理地址
↓
RAM

3.内存以Page为基本单位

Linux不会通常以“1字节”为单位管理物理内存。最基本的单位是:Page(页)

在常见x86-64系统中:

1
1 Page = 4096 bytes

物理内存可以想象成:

1
2
3
4
5
6
7
8
9
10
11
12
RAM
┌──────┐
│ Page │ 4KB
├──────┤
│ Page │ 4KB
├──────┤
│ Page │ 4KB
├──────┤
│ Page │ 4KB
├──────┤
│ ... │
└──────┘

Linux内核使用struct page等数据结构管理这些物理页。因此Linux实际上是在管理大量的物理页框(Page Frame)。


4.整个内存申请流程串起来

假设:

1
2
char *p = malloc(100 * 1024 * 1024);
p[0] = 1;

可以发生这样的事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
       malloc()
│
▼
libc allocator
│
mmap()/brk()
│
▼
建立虚拟地址空间
│
▼
返回地址
│
▼
p[0] = 1
│
▼
Page Fault
│
▼
Kernel处理
│
▼
Buddy System分配Page
│
▼
更新页表
│
▼
CPU继续执行
│
▼
RAM

如果以后内存压力变大:

1
2
3
4
5
6
7
8
RAM压力
↓
Memory Reclaim
├── Page Cache回收
├── Slab回收
└── Anonymous Page → Swap
↓
Disk

如果最终还是不够:

1
2
3
OOM
↓
Kill Process

5.从Linux源码角度看

模块 主要负责
Page Table 虚拟地址→物理地址
MMU/TLB 地址转换和缓存
Page Fault 缺页处理
Buddy Allocator 物理页分配
SLUB 内核对象分配
kmalloc 内核小块内存
vmalloc 虚拟连续、物理不连续内存
mmap 建立用户空间映射
brk 调整进程heap
Page Cache 文件数据缓存
Reclaim 内存回收
LRU 页面活跃度管理
Swap 将部分匿名内存换出
OOM 内存耗尽处理
cgroup 对进程组进行内存资源限制
NUMA 多节点内存管理

最关键的主线其实只有:
虚拟地址→页表→物理Page→Page Fault→内存分配→回收→Swap/OOM。