mlock/mlockall可锁定物理内存页防止换出到swap,但需cap_ipc_lock权限及足够rlimit_memlock配额;普通new/栈分配无法阻止换页,因内核对未显式锁定的用户态内存统一按需换出。

无法完全防止,但可通过 mlock 或 mlockall 锁定物理内存页,避免被换出到 SWAP —— 前提是进程有 CAP_IPC_LOCK 权限(通常需 root 或特定 capability)。
为什么普通 new 或栈数组无法阻止换页
操作系统对用户态内存的管理是透明且统一的:只要内存页未被显式锁定,内核就可能在内存紧张时将其换出到 SWAP。这与你用 int arr[1024] 还是 new int[1024] 无关,也和是否 memset 初始化无关。
- 栈、堆、全局数据段默认都可换页
-
malloc/new分配的是虚拟地址,背后物理页由内核按需映射和回收 - 即使你刚分配完就立刻访问所有元素,也不能保证后续不被换出
用 mlock 锁定指定内存区域
这是最直接可控的方式:对已分配的内存块调用 mlock,告诉内核“这段物理页必须常驻 RAM”。注意它只作用于当前已映射的页,且不会自动扩展到后续新分配的内存。
- 先分配内存(如
new uint8_t[4096]),再立即mlock(ptr, size) - 检查返回值:
mlock失败时返回 -1,errno可能为ENOMEM(超出RLIMIT_MEMLOCK限制)或EPERM(权限不足) - 记得配对调用
munlock(ptr, size),否则造成内存泄漏(锁住的页无法被其他进程复用,且不随delete自动释放) - 示例关键片段:
uint8_t* secret = new uint8_t[4096]; if (mlock(secret, 4096) != 0) { perror("mlock failed"); // 处理失败:降级策略或退出 } // ... 使用 secret ... munlock(secret, 4096); delete[] secret;
用 mlockall 锁定整个进程地址空间
更粗粒度的选择,适合对延迟极度敏感、且能接受资源开销的场景(如实时音频处理、加密密钥长期驻留)。它会锁定当前及未来所有匿名映射(包括堆、未初始化的全局变量),但不影响文件映射(如代码段、共享库)。
-
mlockall(MCL_CURRENT | MCL_FUTURE)是常用组合 - 同样受
RLIMIT_MEMLOCK限制;若进程后续申请大量堆内存,可能因锁页超限导致malloc失败 - 权限要求相同,且一旦调用,子进程(
fork后)不会继承锁定状态 - 没有对应的 “解锁全部” 函数,只能靠进程退出或显式
munlock个别区域
容易被忽略的关键点
权限和配额不是一次性设置完就万事大吉:
-
RLIMIT_MEMLOCK默认极小(通常 64KB),必须提前用setrlimit(RLIMIT_MEMLOCK, &rlim)提升,且该调用本身也需要权限 - 容器环境(如 Docker)中,
--cap-add=IPC_LOCK必须显式添加,否则mlock直接EPERM - 锁定的内存仍可能被
mincore检测到,但无法被swapon换出 —— 不过若系统禁用了 SWAP,mlock就只是防止被 reclaim(如 LRU 驱逐),而非“防交换” - ARM64 等平台对大页锁定支持更复杂,
mlock可能触发隐式大页拆分,影响性能
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











