getpagesize() 不可靠,应使用 POSIX 标准的 sysconf(_SC_PAGESIZE);前者非标准、不支持所有系统,且无法反映大页策略,后者可移植、语义明确,是内存对齐的必备运行时查询。

getpagesize() 返回的值到底靠不靠谱
getpagesize() 是最直接的调用,返回当前系统的内存页面大小(单位字节),在大多数 Linux 和 macOS 上它确实能工作。但它不是 POSIX 标准函数,而是源自 BSD 的扩展,在某些嵌入式或非主流系统上可能未定义,编译时会报 undefined reference to 'getpagesize'。即使链接成功,它的行为也不保证与 mmap 实际使用的页面对齐粒度完全一致——尤其在启用了大页(huge pages)或透明大页(THP)的系统上,getpagesize() 仍只返回基础页面大小(通常是 4096),而非实际分配时可能采用的 2MB 页面。
sysconf(_SC_PAGESIZE) 才是 POSIX 正统做法
用 sysconf(_SC_PAGESIZE) 替代 getpagesize() 更安全、更可移植。它明确属于 POSIX.1 标准,所有合规的 Unix-like 系统都必须支持。返回值含义与 getpagesize() 相同,但语义更清晰:这是系统报告的“最小内存保护和映射单位”。注意两点:
-
_SC_PAGESIZE是宏,需包含<unistd.h></unistd.h> - 失败时返回 -1,应检查 errno(虽然实际中极少失败)
- 它不随运行时启用的大页策略动态变化——仍是基础页大小;若需探测大页能力,得查
/proc/meminfo或用mincore()配合地址试探
示例:
#include <unistd.h>
#include <stdio.h>
long page_size = sysconf(_SC_PAGESIZE);
if (page_size == -1) {
// 处理错误
}
printf("page size: %ld\n", page_size); // 通常输出 4096
为什么 mmap 对齐必须用 sysconf 而不是硬编码 4096
硬编码 4096 看似省事,但在 ARM64(默认 64KB 页面)、某些 PowerPC 或开启 CONFIG_ARM64_64K_PAGES 的内核上会直接导致 mmap 失败并返回 EINVAL。即便 x86_64 主流是 4KB,glibc 的 mmap 实现内部仍依赖 sysconf(_SC_PAGESIZE) 做校验。所以只要涉及 MAP_ALIGNED、手动地址对齐、或 mmap 的 offset 参数,就必须用运行时获取的值。常见错误包括:
立即学习“C++免费学习笔记(深入)”;
- 把
malloc返回地址直接传给mmap(MAP_FIXED)—— 未按页对齐 - 计算
offset时用addr % 4096而非addr % page_size - 在跨平台库中忽略 Windows(它用
GetSystemInfo().dwPageSize)
Linux 下验证实际页面大小的辅助手段
内核参数和运行时状态可能影响有效页面尺寸,仅靠 sysconf 不足以覆盖全部场景。例如透明大页(THP)启用时,malloc 分配的大块内存可能被映射为 2MB 页面,但 sysconf(_SC_PAGESIZE) 仍返回 4096。此时可通过以下方式交叉验证:
- 读取
/proc/self/maps,观察某段内存的mm_struct映射是否标注htlb(huge TLB) - 检查
/proc/meminfo中的HugePages_Total和TransparentHugepage状态 - 用
mincore(&addr, 1, &vec)探测地址所在页是否驻留在大页中(需 root 权限且有精度限制)
这些不属于标准 C++ 接口,属于 Linux 特定调试手段,不能替代 sysconf(_SC_PAGESIZE) 在常规内存对齐中的作用。
真正容易被忽略的是:页面大小不是编译期常量,也不是 CPU 架构固定值,它由内核配置、启动参数和运行时策略共同决定。哪怕同一台机器,重启后加载不同内核模块也可能改变可用页面类型。所以任何涉及内存映射、对齐、缓存行优化的代码,都该把 sysconf(_SC_PAGESIZE) 当作不可省略的初始化步骤。


















