核心是分层控制:用户级用ulimit和limits.conf限制总量,进程级用cgroup v2精确管控物理资源;两者互补,ulimit管启动约束,cgroup v2管实际内存占用与子进程继承,缺一不可。

限制用户的进程数和内存使用,核心是分层控制:用户级用 ulimit 和 /etc/security/limits.conf,进程级用 cgroup v2。前者管“谁启动多少个进程、能占多大内存”,后者管“某个具体进程实际用了多少物理资源”。两者互补,不能互相替代。
用 ulimit 和 limits.conf 限制用户级总量
这是最常用、最稳妥的入口,适用于所有登录会话(SSH、su、图形终端等),由 PAM 模块在用户登录时自动加载。
-
进程数限制:在
/etc/security/limits.conf中添加两行(以用户devuser为例):devuser soft nproc 512devuser hard nproc 1024
软限制可由用户自行调高(但不超过硬限制),硬限制只有 root 能修改。新登录后生效。 -
内存相关限制:注意区分不同内存类型:
devuser soft rss 1048576—— 常驻内存(RSS),单位 KB,即约 1GB;devuser soft as 2097152—— 地址空间(AS),即虚拟内存上限,2GB;
不建议设rss硬限制过低,否则可能触发 OOM killer 杀进程,尤其对 Java 或 Python 应用。 -
验证是否生效:切换到该用户后执行
ulimit -u(查进程数)、ulimit -v(查虚拟内存)、ulimit -m(查 RSS),输出应与配置一致。
用 cgroup v2 精确限制单个进程的物理内存
ulimit 的 -m 实际上在多数现代内核中已被忽略(仅影响 brk 系统调用),真正可靠的内存硬限必须靠 cgroup v2。
-
确认环境就绪:运行
mount | grep cgroup2,确保已挂载;再执行cat /sys/fs/cgroup/cgroup.controllers | grep memory,确认memory在列表中。若没有,执行echo "+memory" | sudo tee /sys/fs/cgroup/cgroup.subtree_control启用。 -
限制正在运行的进程:假设 PID 是
12345,想限制其总内存为 512MB:sudo mkdir /sys/fs/cgroup/limited-procecho 536870912 | sudo tee /sys/fs/cgroup/limited-proc/memory.maxecho 12345 | sudo tee /sys/fs/cgroup/limited-proc/cgroup.procs -
启动新进程时直接加限:更推荐方式,避免手动迁移:
systemd-run --scope -p MemoryMax=512M -p CPUQuota=50% python3 app.py
这样启动的进程及其所有子进程都受控,退出后 cgroup 自动清理。
为什么不能只靠 ulimit?
ulimit 对内存的限制存在明显局限:
-
ulimit -m(RSS)在内核 2.6.32+ 后基本失效,多数发行版默认禁用; -
ulimit -v(AS)限制的是虚拟地址空间,不等于物理内存,malloc 成功不代表真分配了 RAM; - 它无法阻止子进程绕过限制(比如一个 bash 脚本 fork 出 10 个 java 进程,每个都独立计数);
- 对容器、systemd 服务、或通过 cron 启动的任务,ulimit 默认不生效,需额外配置。
组合使用建议
生产环境中建议双层设防:
- 对普通用户,在
limits.conf中设nproc和宽松的as,防止 fork 炸弹或无界内存申请; - 对关键服务(如数据库、Web 服务),用 systemd 单元文件写死
MemoryMax=和TasksMax=,并启用OOMScoreAdjust=-1000避免被误杀; - 临时调试时,用
systemd-run --scope快速套上资源围栏,无需改配置、不扰系统。


















