虚拟内存系统
为了方便管理程序运行时使用的内存空间,
memory management unit(mmu)内存管理单元,辅助操作系统管理内存。
| 内存类型 | 描述 |
|---|---|
| 物理内存 | 实际内存空间 |
| 虚拟内存 | 应用程序可以看到并使用的内存空间 |
内存寻址范围
由地址总线范围和数据总线大小决定
例如:
内存地址总线长度20bit,内存单元8bit,则访问2^20 * 8bit的内存空间,即2MB。
内存地址总线长度32bit,内存单元8bit,则访问2^32 * 8bit的内存空间,即4GB。
地址类型
| 地址类型 | 描述 |
|---|---|
| 逻辑地址 | 机器语言指令使用的地址,指定一个操作数的地址或一条指令的地址 |
| 线性地址 | 线性地址是一个无符号整数,范围0x00~0xffffffff(ffffffff), 由逻辑地址的 段基址+段偏移计算得到若无分页机制,线性地址即物理地址 |
| 物理地址 | 内存单元的实际地址,用于芯片级内存单元寻址 |
地址转换
逻辑地址-->线性地址-->物理地址,实际转换流程由MMU操作
分页机制
将虚拟内存空间和物理内存空间按固定大小划分成若干页
只将程序需要使用的页映射到物理内存
| 类型 | 说明 |
|---|---|
| 页框 | page frame,分页单元把所有的RAM物理内存分成固定长度的页框,长度也是4KB或更大 |
| 页 | 分页单元将线性地址分成以固定长度位单位的组,称为页(page)。页内部连续,映射是以页位基本单位。 |
4级页表
x86_64采用四级页表,来管理标准4KB页。
在x64体系中用48位的虚拟寻址
理论上48位地址长度可以管理512TB的地址空间
不过x86_64四级页表只使用了256TB的虚拟地址空间。
详见linux 4级内存页表虚拟地址空间范围
48bit线性地址划分为下面几部分:
| 字段 | 长度 | 描述 |
|---|---|---|
| OFFSET | 12bit | 页内偏移量,对应4K范围 |
| PT(Page Table) | 9bit | |
| PDT(Page Directory Table) | 9bit | 每个表4K,内含512个PDE结构,每个8字节 |
| PDPT(Page Directory Pointer Table) | 9bit | 及表内的PDPTE结构 |
| PML4T(Page Map Level4 Table) | 9bit | 及表内的PML4E结构 |
| f/0 | 16bit | 全填充0或f |
同x86 32位一样,系统使用CR3存储页表的基地址,即PML4T的物理基地址。x86_64支持多种页面大小:
1)4K页面:使用PML4T,PDPT,PDT和PT 四级页转化表结构; 2)2M页面:使用PML4T,PDPT 和PDT三级页转化表结构; 3)1G 页面:使用PML4T和PDPT二级页表转化结构。 详细的地址转换过程,在下面的linux 系统分页机制实现再阐述。
虚拟地址映射到物理地址的每一个映射叫叫页表实体(page table entry,PTE)。
TLB
translation lookaside buffer,转换旁路缓存。 CPU的MMU使用该缓存存储操作系统最近使用的映射。
转换过程

当虚拟地址需要转换到物理地址时,先查询TLB。 如果TLB命中,则返回对应的物理地址,并继续访问物理内存。 如果不命中,则mmu或者TLB缺失处理程序将会去页表查否是否存在该映射。 如果存在,将其写回到TLB。
TTW,translation table walk,转换表漫游。当tlb中不存在虚拟地址和物理地址的转换关系时,通过访问内存中的转换表来获取虚拟地址和物理地址的对应关系。 TT,转换表,一般为多级页表。
缓存
buffer缓存
块设备的写缓冲,将随机写合并为顺序写,减少磁盘 I/O 次数。
# 查看 buffer/cache 使用量
free -h
cat /proc/meminfo | grep -E "Buffers|Cached"页缓存
磁盘文件内容的内存缓存(Page Cache),读文件时先查 page cache,命中则不访问磁盘。
# 查看 page cache 占用
cat /proc/meminfo | grep -E "Cached|Dirty|Writeback"
# 手动释放页缓存(生产环境慎用)
echo 1 > /proc/sys/vm/drop_caches # 释放 page cache
echo 2 > /proc/sys/vm/drop_caches # 释放 dentries/inodes
echo 3 > /proc/sys/vm/drop_caches # 释放以上所有交换分区(Swap)
当物理内存不足时,内核将部分内存页换出(swap out)到磁盘上的交换分区。
# 查看 swap 状态
swapon --show
free -h
cat /proc/meminfo | grep Swap
# 创建并启用 swap 文件
dd if=/dev/zero of=/swapfile bs=1G count=4
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# 永久生效(写入 /etc/fstab)
echo '/swapfile none swap sw 0 0' >> /etc/fstab
# 关闭 swap
swapoff /swapfileswappiness:控制内核使用 swap 的积极程度,值 0-100,越高越积极。
# 查看当前值(默认 60)
cat /proc/sys/vm/swappiness
# 临时修改
echo 10 > /proc/sys/vm/swappiness
# 永久修改
echo 'vm.swappiness=10' >> /etc/sysctl.conf
sysctl -p硬件缓存
CPU 片上缓存,按访问速度分级:L1 Cache(~1ns)→ L2 Cache(~5ns)→ L3 Cache(~20ns)
区
根据使用功能,将页划分为不同的区。
| 区(Zone) | 说明 |
|---|---|
ZONE_DMA | 可用于 ISA 设备 DMA 的低端内存(< 16MB) |
ZONE_DMA32 | AMD64 上可用于 32 位 DMA 的内存(< 4GB) |
ZONE_NORMAL | 常规映射内存区域 |
ZONE_HIGHMEM | 32 位系统高于 896MB 的物理内存(x86_64 不存在) |
ZONE_MOVABLE | 可迁移页,用于内存热插拔 |
# 查看各 Zone 信息
cat /proc/zoneinfo内存分配
物理内存分配
内核通过**伙伴系统(Buddy System)**分配连续物理页,按 2^n 页为单位管理。
# 查看伙伴系统状态
cat /proc/buddyinfoslab 分配器:在伙伴系统之上,为内核小对象(task_struct、inode 等)提供高效分配。
# 查看 slab 使用情况
slabtop
cat /proc/slabinfo用户空间内存分配
| 分配来源 | 说明 |
|---|---|
malloc() | C 库分配,小块用 brk/sbrk 扩堆,大块用 mmap |
mmap() | 直接向内核申请内存映射区域 |
brk()/sbrk() | 移动堆顶指针 |
OOM Killer
当系统内存严重不足时,OOM Killer(Out-Of-Memory Killer)会选择并杀死某个进程以释放内存。
OOM 触发与日志
# 查看 OOM 相关内核日志
dmesg | grep -i "oom\|out of memory\|killed process"
journalctl -k | grep -i oomoom_score
内核根据进程的内存使用、运行时间等计算 oom_score(0-1000),分数越高越容易被杀。
# 查看进程的 oom_score
cat /proc/<pid>/oom_score
# 查看 oom_score_adj(调整值,范围 -1000~1000)
cat /proc/<pid>/oom_score_adj保护关键进程
# 设置为 -1000 可完全免除 OOM 杀死(需 root)
echo -1000 > /proc/<pid>/oom_score_adj
# 或通过 systemd 服务配置
# /etc/systemd/system/myservice.service
[Service]
OOMScoreAdjust=-1000禁用 OOM Killer(极端情况,慎用)
# 禁用(系统可能挂死)
echo 1 > /proc/sys/vm/panic_on_oom
# 内存不足时触发 panic 并重启
echo 1 > /proc/sys/kernel/panic_on_oops内存监控命令
# 综合概览
free -h
# 详细内存信息
cat /proc/meminfo
# 进程内存使用(VSZ: 虚拟内存,RSS: 常驻内存)
ps aux --sort=-%mem | head -15
# 进程内存映射详情
pmap -x <pid>
cat /proc/<pid>/maps
cat /proc/<pid>/smaps
# 实时内存监控
vmstat 1
top (按 M 按内存排序)
htop
# 内存使用统计(按进程)
smem -r -k | head -20Linux的进程地址空间一,兰新宇
Linux的进程地址空间二,兰新宇
Four-level page tables
Five-level page tables