怎样锁定内存页防止交换?

wen IT资讯 361

本文目录导读:

怎样锁定内存页防止交换?

  1. 核心系统调用
  2. 权限与限制
  3. 应用场景与注意事项
  4. 检查已锁定的内存
  5. 完整示例(C语言)
  6. 编程注意事项:mlock 的位置

在 Linux 系统中,锁定内存页以防止其被交换到磁盘(swap)上,通常使用 mlock()mlockall() 系统调用,以下是详细的方法和注意事项:

核心系统调用

a) mlock():锁定指定的内存区域

#include <sys/mman.h>
int mlock(const void *addr, size_t len);
  • 作用:锁定从 addr 开始的 len 字节的内存页,使其常驻物理内存。
  • 示例(锁定一个缓冲区):
    char *buf = malloc(4096);
    if (mlock(buf, 4096) == -1) {
        perror("mlock failed");
    }

b) mlockall():锁定进程的全部内存页

#include <sys/mman.h>
int mlockall(int flags);
  • flags 可选值:
    • MCL_CURRENT:锁定当前已分配的所有内存页。
    • MCL_FUTURE:锁定未来分配的所有内存页(例如后续的 mallocmmap 等)。
    • MCL_ONFAULT(Linux 4.4+):仅在页被访问时锁定(按需锁定,避免一次性锁定大量未使用内存)。
  • 示例(锁定所有当前和未来的内存):
    if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
        perror("mlockall failed");
    }

c) 解锁:munlock()munlockall()

  • munlock(addr, len):解锁指定区域。
  • munlockall(void):解锁当前进程的所有锁定的内存。

权限与限制

  • 需要 CAP_IPC_LOCK 能力:非 root 用户默认不能锁定任意大量内存,可以通过以下方式授权:
    • 以 root 运行程序。
    • 为用户设置 ulimit -l unlimited(临时)或配置 /etc/security/limits.conf(永久):
      username hard memlock unlimited
      username soft memlock unlimited
  • 受 RLIMIT_MEMLOCK 限制ulimit -l 设置单个用户可锁定的最大内存量。

应用场景与注意事项

适用场景

  • 实时系统:需要保证关键数据始终在物理内存中,避免换页延迟。
  • 密码/密钥处理:防止敏感数据被交换到磁盘(虽然仍有其他攻击面)。
  • 大数据处理:锁定频繁访问的缓存或数据结构。

注意事项

  • 内存耗尽风险:锁定过多内存会导致系统内存不足,甚至 OOM(Out-Of-Memory)杀进程。
  • 子进程行为:锁定的内存在 fork() 时不会被子进程继承(子进程需重新调用 mlock)。
  • 性能影响:锁定内存会减少可用于文件缓存的内存,可能影响整体系统性能。
  • 动态内存管理:如果使用 malloc 后调用 mlock,确保锁定的地址和长度正确(malloc 可能多分配页对齐的内存)。

检查已锁定的内存

查看 /proc/[pid]/status 中的 VmLck 字段:

grep VmLck /proc/1234/status

或者用 pmap

pmap -x 1234 | grep locked

完整示例(C语言)

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
int main() {
    size_t size = 1024 * 1024; // 1 MB
    char *buf = malloc(size);
    // 锁定内存
    if (mlock(buf, size) == -1) {
        perror("mlock");
        exit(1);
    }
    // 访问内存(触发缺页,确保锁定生效)
    memset(buf, 0, size);
    printf("Memory locked. PID: %d\n", getpid());
    printf("Check with: grep VmLck /proc/%d/status\n", getpid());
    // 模拟工作
    sleep(30);
    // 解锁
    munlock(buf, size);
    free(buf);
    return 0;
}

编程注意事项:mlock 的位置

  • 在分配内存后立即调用 mlock,除非使用 mlockall(MCL_FUTURE)
  • 对于匿名映射(如 mmap(MAP_ANONYMOUS)),同样可以使用 mlock
  • 避免锁定栈内存:栈会动态增长,难以精确控制,通常使用 mlockall 来锁定全部内存更简单。
需求 方法
锁定一小块关键数据 mlock(addr, len)
锁定整个进程内存(包括未来分配) mlockall(MCL_CURRENT \| MCL_FUTURE)
按需锁定(延迟锁定) mlockall(MCL_ONFAULT)
解锁 munlock / munlockall

重要:锁定内存是特权操作,务必在设计中评估内存使用量,并设置合理的 ulimit 限制,避免系统不稳定。

抱歉,评论功能暂时关闭!