mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
2999 字
8 分钟
为什么 Linux 默认页大小是 4KB
2023-09-21

Linux 系统中的页面大小(Page Size)默认是 4KB。这个数字看起来平淡无奇,但它背后是硬件约束、历史兼容、性能权衡等多重因素的综合结果。为什么不是 1KB?为什么不是 64KB?4KB 这个”恰好”的数字,藏着怎样的设计考量?

一、页面大小的基础概念#

1.1 什么是页#

虚拟内存被分割成固定大小的块,称为页面(Page)。物理内存同样被分割成等大小的页框(Page Frame)。操作系统通过页表将虚拟页面映射到物理页框:

虚拟地址空间: 0x00000000 - 0xFFFFFFFF (4GB)
页面大小: 4KB = 4096 字节
页面数量: 4GB / 4KB = 1,048,576 页
虚拟地址结构(以 32 位系统为例):
┌──────────────┬───────────────┐
│ 页号 (20位) │ 页内偏移 (12位) │
└──────────────┴───────────────┘
2^20 = 1M 页 2^12 = 4KB 偏移

4KB 的页大小意味着虚拟地址的低 12 位是页内偏移,高位是页号。这个划分直接影响页表的大小和 TLB 的效率。

1.2 页表的工作方式#

flowchart LR VA[虚拟地址] -->|分段| VPN[页号] + VPO[页内偏移] VPN -->|查找| PT[页表] PT -->|获取| PPN[物理页号] PPN -->|拼接| PA[物理地址] VPO --> PA

页表是虚拟内存的核心数据结构。每个页表项(PTE)通常占 4 字节(32 位系统)或 8 字节(64 位系统),存储物理页号和访问权限等信息。

1.3 TLB:页表的缓存#

TLB(Translation Lookaside Buffer)是 CPU 内部的页表缓存,缓存最近使用的虚拟地址到物理地址的映射。每次内存访问,CPU 先查 TLB,命中则直接得到物理地址;未命中则需要遍历页表(Page Table Walk),代价高昂。

TLB 的容量有限。以 Intel Core i7 为例:

TLB 层级容量覆盖范围(4KB 页)
L1 ITLB64 项256 KB
L1 DTLB64 项256 KB
L2 STLB1536 项6 MB

页大小直接决定 TLB 能覆盖的内存范围。4KB 页下,1536 项 TLB 只能覆盖 6MB。如果程序的工作集大于 6MB,就会出现 TLB miss,性能下降。

二、4KB 的历史渊源#

2.1 早期计算机的约束#

年份典型系统内存大小页大小选择原因
1964IBM 3601-2 MB4KB磁芯存储器的管理粒度
1977VAX-11/7804-16 MB512B小内存下的精细管理
1985Intel 3864-16 MB4KB兼容 IBM 体系
1993Pentium16-64 MB4KB沿用 386 页大小

IBM System/360 是最早采用 4KB 页大小的主流系统之一。这个选择并非偶然:1960 年代的磁芯存储器以 4096 字节为一个管理单位,4KB 正好对应这一硬件粒度。

Intel 386 处理器(1985 年)引入分页机制时选择了 4KB,这个决定直接影响了后续所有 x86 处理器,进而影响了 Linux 的设计。

2.2 DEC VAX 的反面教材#

VAX-11/780 使用 512 字节的页大小。在 4MB 内存的机器上,512 字节页需要的页表项数是 4KB 页的 8 倍。页表本身占用的内存比例过高,成为系统的沉重负担。

VAX 后来在 VAX-11/785 中将页大小提升,但 512 字节的设计已经被锁定在指令集架构中,无法更改。这是一个反面教训:页大小一旦确定,就极难改变。

2.3 为什么不是 2 的其他幂次?#

页大小优点缺点为什么没选
512B内部碎片极少页表巨大,TLB 覆盖极小VAX 的失败经验
1KB碎片少页表偏大不如 4KB 平衡
2KB碎片较少页表中等硬件不支持
4KB综合最优大内存场景 TLB 不足被广泛采用
8KB页表减半碎片增加部分架构使用
16KB页表更小碎片明显arm64 可选
64KBTLB 覆盖大碎片严重arm64 可选/大型机

4KB 恰好处于”页表不太大、碎片不太严重”的甜蜜点。

三、页面大小的权衡分析#

3.1 内部碎片#

内部碎片(Internal Fragmentation)是指页面中未被使用的空间。假设进程需要 1 字节,操作系统也必须分配一个完整页面。

假设进程分配了 N 个页面,每个页面平均浪费一半:
- 4KB 页:平均浪费 2KB/页
- 64KB 页:平均浪费 32KB/页
对于大量小对象(如文件元数据、网络连接缓冲区),
浪费更严重。

定量分析:假设一个系统有 1000 个进程,每个进程平均有 20 个部分填充的页面:

页大小每页浪费总浪费
4KB~2KB~40MB
64KB~32KB~640MB

在内存紧张的嵌入式系统上,640MB 的浪费是不可接受的。

3.2 页表大小#

页表大小取决于虚拟地址空间和页大小的比值:

页大小页表项数(4GB 空间)页表大小(每项 8B)占 4GB 比例
4KB1,048,5768 MB0.2%
16KB262,1442 MB0.05%
64KB65,536512 KB0.01%
2MB2,04816 KB0.0004%

4KB 页的页表占 8MB,对现代系统可以接受。但在 64 位系统上,虚拟地址空间远大于 4GB(通常是 48 位或 57 位),页表问题变得严峻:

# 64 位 Linux 的虚拟地址空间
# 典型配置:48 位虚拟地址 = 256 TB
# 4KB 页:256TB / 4KB = 64G 页表项
# 如果全部映射,页表本身需要 ~512GB!
# 这就是为什么 x86_64 使用四级页表
# 每级只映射实际使用的部分,避免分配完整页表

3.3 TLB 覆盖范围#

TLB 是页面大小选择中最关键的硬件因素。TLB 容量有限,页大小直接决定 TLB 能覆盖多少内存:

页大小L2 STLB 1536 项覆盖对数据库的影响
4KB6 MB频繁 TLB miss
2MB3 GB大幅减少 miss
1GB1.5 TB几乎不 miss

这就是为什么数据库(如 Oracle、PostgreSQL)强烈推荐使用 HugePages。

3.4 I/O 效率#

页面大小也影响磁盘 I/O 的效率:

# 磁盘读取一页的开销 = 寻道时间 + 旋转延迟 + 传输时间
# 假设 HDD 参数:
# 寻道时间:5ms
# 旋转延迟:4ms (7200 RPM)
# 传输速率:100MB/s
# 读取 4KB:5 + 4 + 0.04 = 9.04ms(传输只占 0.4%)
# 读取 64KB:5 + 4 + 0.64 = 9.64ms(传输只占 6.6%)
# 读取 1MB:5 + 4 + 10 = 19ms(传输占 52%)
# 页面越大,每次 I/O 传输的数据越多,利用率越高
# 但也意味着读入更多不需要的数据(预读浪费)

4KB 的页大小在机械硬盘时代是一个合理的 I/O 粒度,但在 SSD 时代显得偏小。SSD 的 4K 随机读性能远好于 HDD,但连续读仍然更高效。

四、4KB 与磁盘块大小的关系#

4.1 磁盘扇区的历史#

磁盘的最小读写单位是扇区(Sector),历史上是 512 字节。现代磁盘(包括 SSD)已经开始使用 4K 物理扇区(Advanced Format):

时期扇区大小原因
1980-2010512 字节兼容 DOS 时代
2010-至今4K 物理扇区提高纠错效率,减少开销

当页面大小(4KB)等于磁盘物理扇区大小(4KB)时,一个页面的读写恰好对应一个磁盘扇区,避免了读写放大(Read-Modify-Write)问题。这并非巧合,而是硬件和软件协同演进的结果。

4.2 文件系统块大小#

# 查看 ext4 文件系统的块大小
sudo tune2fs -l /dev/sda1 | grep "Block size"
# 输出:Block size: 4096
# ext4 默认块大小也是 4KB
# 这与页大小对齐,使得一个文件系统块恰好对应一个内存页
# 大大简化了页面缓存的实现

文件系统块大小与页大小对齐是一个重要的设计原则。如果两者不匹配,一个页面可能跨越多个文件系统块,或者一个文件系统块被拆分到多个页面,增加了实现复杂度。

五、x86_64 的多级页表与 4KB#

5.1 四级页表结构#

x86_64 使用四级页表(48 位虚拟地址):

虚拟地址(48 位有效):
┌─────┬─────┬─────┬─────┬──────────┐
│ PML4│ PDPT│ PD │ PT │ Offset │
│9位 │9位 │9位 │9位 │12位 │
└─────┴─────┴─────┴─────┴──────────┘
每级 512 项 页内偏移
4KB 页 + 四级页表:
- 每个页表占 4KB(512 项 × 8 字节/项)
- 每个页表恰好一页,管理方便

注意:每个页表恰好占 4KB,包含 512 个 8 字节的页表项。这不是巧合——页大小决定了页表的大小,页表又恰好占一页。这种自相似的设计简化了内存管理。

5.2 5 级页表(LA57)#

2017 年,Intel 引入了 5 级页表(Linear Address 57,LA57),将虚拟地址从 48 位扩展到 57 位:

# 检查是否支持 5 级页表
# Linux 4.14+ 支持
cat /proc/cpuinfo | grep la57
# 5 级页表虚拟地址空间:
# 48 位:256 TB
# 57 位:128 PB
# 对大内存服务器(TB 级)非常有意义

5 级页表增加了一级,但每一级页表仍然占 4KB。4KB 的基本粒度没有改变。

5.3 用代码查看当前页大小#

// C 程序获取页大小
#include <unistd.h>
#include <stdio.h>
int main() {
long page_size = sysconf(_SC_PAGESIZE);
printf("Page size: %ld bytes\n", page_size);
// 输出:Page size: 4096 bytes
return 0;
}
# Shell 中查看页大小
getconf PAGESIZE
# 输出:4096
# 或者
cat /proc/sys/vm/page_size # 某些系统
# 或者
pagesize

六、现代系统的大页支持#

6.1 为什么需要大页#

4KB 页在 64 位系统上的主要问题是 TLB 覆盖不足。以一个使用 10GB 内存区域的数据库为例:

4KB 页:
- 需要的 TLB 项:10GB / 4KB = 2,621,440 项
- L2 STLB 容量:~1536 项
- TLB 命中率:极低
2MB 大页:
- 需要的 TLB 项:10GB / 2MB = 5120 项
- L2 STLB 容量:~1536 项
- TLB 命中率:仍然不足,但好很多
1GB 大页:
- 需要的 TLB 项:10GB / 1GB = 10 项
- L2 STLB 容量:~1536 项
- TLB 命中率:接近 100%

6.2 静态 HugePages#

# 查看 HugePages 配置
cat /proc/meminfo | grep -i huge
# HugePages_Total: 0
# HugePages_Free: 0
# Hugepagesize: 2048 kB
# 预分配 HugePages(需要 root)
echo 1024 > /proc/sys/vm/nr_hugepages
# 分配 1024 个 2MB 大页 = 2GB 大页内存
# 在程序中使用 HugePages
# 方法 1:shmget with SHM_HUGETLB
# 方法 2:mmap with MAP_HUGETLB
# 方法 3:mount hugetlbfs 文件系统

静态 HugePages 的缺点是需要预先分配,不能被换出,缺乏灵活性。

6.3 透明大页(Transparent HugePages,THP)#

# 查看透明大页状态
cat /sys/kernel/mm/transparent_hugepage/enabled
# [always] madvise never
# always:内核自动尝试使用大页
# madvise:只在应用通过 madvise(MADV_HUGEPAGE) 请求时使用
# never:禁用透明大页
# 查看 THP 使用情况
cat /sys/kernel/mm/transparent_hugepage/defrag

THP 的优势是无需修改应用程序,内核在后台自动将 4KB 页合并为 2MB 大页。但它也有问题:

  • 内存碎片化:长时间运行后,物理内存碎片化,无法找到连续的 2MB 区域
  • 延迟尖峰:khugepaged 线程在后台合并页面时可能引起延迟
  • 调试困难:大页的分配和换出行为不如 4KB 页可预测

6.4 数据库与大页#

# PostgreSQL 推荐配置 HugePages
# postgresql.conf:
# huge_pages = try # 尝试使用 HugePages
# 计算需要的 HugePages 数量
# shared_buffers = 4GB
# 4GB / 2MB = 2048 个 HugePages
# 需要预留一些额外页给其他共享内存
echo 2300 > /proc/sys/vm/nr_hugepages
# Redis 使用 HugePages 的注意事项
# Redis 官方建议:如果不使用 THP,可以直接使用静态 HugePages
# 但 THP 可能导致 fork 延迟(Copy-on-Write 时大页的复制代价高)

七、不同架构的页大小选择#

7.1 主流架构对比#

架构默认页大小支持的页大小大页支持
x864KB4KB4MB
x86_644KB4KB2MB, 1GB
arm644KB4KB, 16KB, 64KB对应大页
PowerPC64KB4KB, 64KB, 16MB16MB, 16GB
IA-6416KB4KB-256KB多种大页
RISC-V4KB4KB2MB, 1GB

7.2 arm64 的 16KB 页选项#

Apple Silicon(M1/M2/M3)使用 16KB 页大小,而非 4KB:

# macOS (Apple Silicon) 上的页大小
pagesize
# 输出:16384(16KB)
# Linux arm64 可以在编译时选择页大小
# CONFIG_ARM64_PAGE_SHIFT=12 → 4KB
# CONFIG_ARM64_PAGE_SHIFT=14 → 16KB
# CONFIG_ARM64_PAGE_SHIFT=16 → 64KB
# 16KB 页的优势:
# - TLB 覆盖范围扩大 4 倍
# - 页表项数减少 4 倍
# - 更好的压缩率(更多对象可放入同一页)
# 16KB 页的劣势:
# - 内部碎片增加
# - 与 x86 生态的默认配置不兼容

Apple 选择 16KB 页是为了在保持内存效率的同时提升 TLB 覆盖范围,这在内存带宽敏感的 SoC 上尤为重要。

7.3 为什么 Linux 不切换默认页大小#

Linux 保持 4KB 默认页大小,原因并非技术惯性那么简单:

  1. ABI 兼容性:很多用户态程序假设页大小是 4KB(如 sysconf(_SC_PAGESIZE) 的返回值),改变会导致兼容性问题
  2. 二进制兼容:在 64KB 页的系统上,4KB 对齐的代码可能无法正确运行
  3. 跨架构一致性:Linux 支持数十种架构,统一默认页大小简化了开发和测试
  4. 渐进式改进:通过 THP 和 HugePages,可以在不改变默认页大小的情况下获得大页的好处

八、页面大小对实际应用的影响#

8.1 内存密集型应用#

应用类型推荐页大小原因
数据库2MB/1GB大工作集,TLB miss 代价高
虚拟机2MBGuest 物理地址映射开销大
科学计算2MB大数组遍历,TLB 压力大
Web 服务器4KB小对象多,碎片化严重
嵌入式系统4KB内存紧张,碎片浪费不可接受

8.2 Copy-on-Write 的页大小影响#

Linux 的 fork() 使用 Copy-on-Write(COW)机制,子进程共享父进程的页面,只在写入时复制。

4KB 页 COW:
- 修改 1 字节只需要复制 4KB 页面
- Redis fork 子进程做 RDB 快照时,内存开销小
2MB 大页 COW:
- 修改 1 字节需要复制 2MB 页面
- 如果大量页面被修改,内存暴增
- 这就是 Redis 在使用 THP 时延迟飙升的原因
# Redis 官方建议禁用 THP
echo never > /sys/kernel/mm/transparent_hugepage/enabled
# 原因:Redis 使用 fork + COW 做持久化
# THP 导致 COW 时复制 2MB 而非 4KB
# 在写入密集场景下,内存使用量可能翻倍

8.3 mmap 和页对齐#

// mmap 的偏移必须是页大小的整数倍
#include <sys/mman.h>
#include <fcntl.h>
int fd = open("large_file.dat", O_RDONLY);
// 正确:偏移 4096 = 1 × 4KB
char *p1 = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, fd, 4096);
// 错误:偏移 1000 不是页对齐的
// char *p2 = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, fd, 1000);
// 报错:EINVAL

页大小影响所有涉及内存映射的接口。在 64KB 页的系统上,mmap 的偏移必须是 64KB 的倍数,这可能影响文件格式的兼容性。

九、总结#

9.1 为什么 Linux 默认 4KB?#

原因说明
历史兼容从 IBM 360 到 x86,4KB 被广泛验证
平衡设计内部碎片和页表大小的最佳折中点
硬件支持所有主流 CPU 架构都支持 4KB 页
磁盘对齐4K 物理扇区与 4KB 页大小对齐
自相似性页表本身恰好占一页,简化内存管理
渐进优化大页作为 4KB 的补充,而非替代

9.2 页面大小选择的权衡#

因素大页面小页面
内部碎片多(浪费空间)少(节省空间)
页表大小小(节省内存)大(占用内存)
TLB 命中率高(覆盖范围大)低(覆盖范围小)
内存利用率低(小对象浪费多)高(精细分配)
COW 开销高(复制代价大)低(复制代价小)
I/O 效率高(单次传输多)低(需多次传输)

核心观点:4KB 是经过历史验证的平衡选择,满足大多数场景的需求。它既不是最优解(不存在最优解),也不是偶然选择,而是硬件演进、软件兼容和工程权衡共同塑造的结果。对于特殊场景,Linux 提供了 HugePages 和 THP 作为补充。

参考资料#

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

为什么 Linux 默认页大小是 4KB
https://blog.souloss.com/posts/why-the-design/why-linux-default-page-size-is-4kb/
作者
Souloss
发布于
2023-09-21
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时