memlock 是 Linux 内核对单个进程通过 mlock() 可锁定物理内存的上限,非总内存限制;ulimit -l 仅临时生效于当前 shell 子进程,systemd/容器等场景需在 service 文件中配 LimitMEMLOCK=,且须避免设过大导致因内存碎片而 mlock 失败。
memlock 是什么,为什么不能只靠 ulimit -l 临时设
memlock 不是某个服务的配置项,而是 linux 内核对单个进程能用 mlock() 或 mlockall() 锁定的物理内存上限。它管的是“你代码里调用 mlock() 能锁多少”,不是“你总共能分配多少内存”。临时执行 ulimit -l 65536 只影响当前 shell 启动的子进程,一旦你用 systemd 启服务、进容器、或被 supervisor 管理,这个设置就失效了。
- 软限制(
soft memlock)是进程实际能锁定的上限;硬限制(hard memlock)是软限制能被setrlimit()提高的天花板 - Redis、DPDK 应用、实时音频处理等启动时会检查
RLIMIT_MEMLOCK,低于所需值直接 abort 并报错Cannot allocate memory或Failed to mlock memory - 某些发行版(如 Ubuntu 22.04+)默认启用
systemd-user会覆盖/etc/security/limits.conf,必须额外配DefaultLimitMEMLOCK=才生效
/etc/security/limits.conf 配置为什么经常不生效
最常见的失效原因是:你改了 limits.conf,但没确认 PAM 是否加载了 pam_limits.so。CentOS/RHEL 默认加载,但 Debian/Ubuntu 的 common-session 和 common-session-noninteractive 里可能漏掉这一行。
- 检查是否加载:
grep pam_limits /etc/pam.d/common-session*,应看到类似session required pam_limits.so - 用户级配置要写全:
redis soft memlock 1048576和redis hard memlock 1048576(单位是 KB,即 1GB),不能只写*—— systemd 服务默认以 root 或专用用户运行,不受通配符影响 - 改完必须重新登录(SSH 重连)或重启服务;
su - redis不触发 PAM session 重载,不可靠
systemd 服务怎么正确设置 memlock
如果你的服务由 systemd 管理(99% 的生产场景),/etc/security/limits.conf 对它基本无效。必须在 service 文件里显式声明。
- 编辑服务文件:
sudo systemctl edit redis-server(或对应服务名) - 写入:
[Service] LimitMEMLOCK=1073741824
(单位是字节;也可写1G,systemd 支持后缀) - 注意:不要用
MemoryLock=true—— 这是 cgroup v2 的内存锁定开关,和RLIMIT_MEMLOCK完全无关,混用会导致行为不可预测 - 生效后验证:
systemctl show redis-server | grep LimitMEMLOCK,再看进程实际值:cat /proc/$(pidof redis-server)/status | grep -i locked
memlock 设太大反而导致 mlock 失败?
是的。内核不会因为你设了 2GB 就真给你留出 2GB 连续物理页 —— mlock() 成功的前提是:请求的内存页当前已在 RAM 中,且内核能立刻完成页表映射。如果系统空闲内存碎片化严重,或可用页数不足(比如只剩 500MB 连续页),即使 ulimit -l 显示 2GB,mlock() 仍会返回 -1 并设 errno = ENOMEM。
- 典型错误日志:
mlock failed: Cannot allocate memory,但free -h显示还有 3GB 空闲 —— 这说明缺的是“连续物理页”,不是总内存 - 排查手段:
cat /proc/buddyinfo查看各阶空闲页数量;echo 1 > /proc/sys/vm/compact_memory可临时触发内存规整(慎用,有延迟) - 真正稳的做法:提前预分配大页(
hugetlb),再用mmap(..., MAP_HUGETLB)分配,绕过普通页的碎片问题
memlock 的本质不是“划一块内存给你”,而是“允许你把已分配的内存钉在 RAM 里”。它的上限、生效路径、底层约束,每一层都可能断在不同地方——调得太高不行,设得太隐晦也不行,最稳妥的方式永远是:按需设、逐层验、看 /proc/PID/status 里的 MMUPageSize 和 Locked 字段说话。


















