答案:Linux通过cgroups限制进程资源使用,可防止资源耗尽、保障服务稳定性。具体通过挂载cgroup文件系统,创建控制组并配置CPU、内存等限制(如cpu.cfs_quota_us、memory.limit_in_bytes或cgroup v2的cpu.max、memory.max),再将进程PID写入tasks或cgroup.procs文件实现。cgroups v1为多层级结构,v2为统一层级,更推荐新系统使用。结合systemd可声明式管理资源限制,通过Service单元设置CPUQuota、MemoryLimit等参数,实现持久化、自动化配置,提升系统可靠性与可维护性。

在Linux系统中,要限制进程的资源使用,核心机制就是cgroups (control groups)。它允许我们将进程组织成不同的组,然后对这些组进行资源分配和限制,比如CPU时间、内存、磁盘I/O和网络带宽等。这就像给不同的部门划定预算,确保每个部门都在自己的“配额”内运行,避免某个部门无限制地占用公司资源。
解决方案
要手动配置cgroups来限制进程资源,通常涉及以下几个步骤。这有点像在文件系统里直接操作,虽然有点底层,但能让你明白其原理。
确认cgroup文件系统已挂载: 大多数现代Linux发行版都会默认挂载cgroup文件系统。你可以通过
mount -t cgroup
或mount -t cgroup2
来查看。例如,你会看到类似/sys/fs/cgroup/cpu
、/sys/fs/cgroup/memory
这样的路径,这些就是不同资源控制器的挂载点。如果你的系统是较新的,可能会看到统一的cgroup2
挂载点/sys/fs/cgroup
。创建控制组: 在相应的cgroup子系统目录下创建一个目录,这个目录就代表你的控制组。 比如,要在CPU子系统下创建一个名为
my_limit_group
的组:sudo mkdir /sys/fs/cgroup/cpu/my_limit_group
同样,对于内存:sudo mkdir /sys/fs/cgroup/memory/my_limit_group
-
配置资源限制: 进入你创建的控制组目录,你会看到一些文件,通过修改这些文件来设定限制。
-
CPU限制 (cgroup v1 示例):
cpu.cfs_period_us
和cpu.cfs_quota_us
配合使用,定义一个CPU周期内,该组最多能使用多少CPU时间。 假设周期是100ms (100000微秒),你想限制一个进程组只能使用一个CPU核心的50%:echo 100000 > /sys/fs/cgroup/cpu/my_limit_group/cpu.cfs_period_us
echo 50000 > /sys/fs/cgroup/cpu/my_limit_group/cpu.cfs_quota_us
这意味着在每100ms内,这个组的进程最多只能运行50ms。如果你只是想设置CPU份额(相对权重),可以使用
cpu.shares
:echo 512 > /sys/fs/cgroup/cpu/my_limit_group/cpu.shares
默认值是1024。如果你有两个组,一个512,一个1024,那么在CPU资源紧张时,后者会获得前者的两倍CPU时间。 内存限制 (cgroup v1 示例):
memory.limit_in_bytes
用于设置内存使用上限。echo 536870912 > /sys/fs/cgroup/memory/my_limit_group/memory.limit_in_bytes
这会将内存限制设置为512MB (512 1024 1024 字节)。memory.swappiness
也可以调整,控制该组进程的内存回收策略。cgroup v2 示例 (CPU和内存): cgroup v2的配置方式略有不同,它通常在一个统一的层级下。
sudo mkdir /sys/fs/cgroup/my_limit_group
(创建组)echo "+cpu +memory" > /sys/fs/cgroup/my_limit_group/cgroup.subtree_control
(启用控制器)echo 50000 > /sys/fs/cgroup/my_limit_group/cpu.max
(CPU限制,格式为quota period
,这里是50000微秒/100000微秒)echo 536870912 > /sys/fs/cgroup/my_limit_group/memory.max
(内存限制)
-
-
将进程添加到控制组: 将目标进程的PID写入到控制组目录下的
tasks
(cgroup v1) 或cgroup.procs
(cgroup v2) 文件中。 假设你的进程PID是12345
:echo 12345 > /sys/fs/cgroup/cpu/my_limit_group/tasks
echo 12345 > /sys/fs/cgroup/memory/my_limit_group/tasks
或者对于cgroup v2:echo 12345 > /sys/fs/cgroup/my_limit_group/cgroup.procs
你也可以在新启动一个进程时直接将其放入某个cgroup,比如使用
cgexec
命令(需要安装cgroup-tools
包)。
为什么需要限制Linux进程的CPU和内存资源?
这问题,说实话,我个人在生产环境里,就遇到过因为某个日志服务突然飙升内存,直接把整个机器拖垮的惨痛教训。当时整个系统都卡死了,SSH都连不上,最后只能硬重启。所以,限制资源真的不是小题大做,它直接关系到系统的稳定性和可用性。
从更深层次看,限制资源有几个非常实际的理由:
- 防止“流氓”进程: 任何一个有bug的程序,或者配置不当的服务,都可能在某个时刻失控,无限占用CPU或内存。如果不加限制,它会迅速耗尽系统资源,导致其他正常服务无法运行,甚至整个系统崩溃。这就像一个水龙头突然爆裂,如果没有阀门,整个屋子都会被淹。
- 保证核心服务SLA: 在一台服务器上跑多个服务是常态。有些服务是核心业务,需要稳定的性能;有些可能只是辅助性的。通过cgroups,你可以给核心服务更高的资源优先级或独占部分资源,即使其他服务出现波动,也不会影响到关键业务的响应速度和稳定性。
- 资源公平性与隔离: 特别是在多租户或多用户环境中,限制资源能确保每个用户或每个应用都只能使用分配给它的那部分资源,避免“邻居效应”。这在容器技术(如Docker)中体现得淋漓尽致,每个容器其实就是一个被cgroup隔离的进程组。
- 成本控制与容量规划: 在云环境中,精确的资源管理能帮助你更好地进行容量规划,避免不必要的资源浪费,从而降低运营成本。你能够更准确地知道一个服务需要多少资源,而不是简单地给它一个超大的虚拟机。
理解cgroups v1与v2的主要区别与选择
cgroups这东西,一开始接触,v1的独立层级确实让人有点懵,感觉像是一堆独立的开关。每个控制器(CPU、内存、IO等)都有自己独立的层级结构,你可以把一个进程放在不同的控制器层级下的不同组里。这导致一个进程可能在CPU控制器下属于A组,在内存控制器下属于B组,管理起来有点混乱。
但cgroups v2出来后,那种统一管理的思路就清晰多了,虽然上手可能需要一点点适应,但长远看绝对是趋势。
cgroups v1 (传统版本):
- 多层级结构: 每个资源控制器(如cpu、memory、blkio)都有自己独立的cgroup层级。
- 进程归属: 一个进程可以在不同的控制器层级中属于不同的cgroup。
- 控制器互不干涉: 不同的控制器之间关联性不强,配置起来相对分散。
- 兼容性: 历史悠久,很多老系统和一些工具仍在使用。
cgroups v2 (统一版本):
- 统一层级结构: 只有一个统一的cgroup层级树。所有的控制器都附加到这个单一的层级上。
- 进程归属: 一个进程只能属于一个cgroup,且其所有资源限制都由该cgroup及其父cgroup共同决定。
- 控制器协调: 设计上考虑了控制器之间的协调性,例如,一个cgroup如果启用了某个子控制器,那么其子cgroup必须也启用该控制器。
-
更清晰的模型: 简化了管理,尤其是在容器和虚拟机场景下,更符合资源隔离的直觉。例如,
cpu.max
替代了cpu.cfs_quota_us
和cpu.cfs_period_us
的组合,更加直观。 - 资源委托: 允许将cgroup的子树委托给非root用户或容器运行时管理,增强了安全性。
如何选择?
免费 盛世企业网站管理系统(SnSee)系统完全免费使用,无任何功能模块使用限制,在使用过程中如遇到相关问题可以去官方论坛参与讨论。开源 系统Web代码完全开源,在您使用过程中可以根据自已实际情况加以调整或修改,完全可以满足您的需求。强大且灵活 独创的多语言功能,可以直接在后台自由设定语言版本,其语言版本不限数量,可根据自已需要进行任意设置;系统各模块可在后台自由设置及开启;强大且适用的后台管理支
-
新部署和容器化环境: 强烈推荐使用cgroups v2。现代Linux发行版(如CentOS 8+, Ubuntu 20.04+, Debian 10+)和新的容器运行时(如
containerd
、crun
)都倾向于或已经默认使用v2。它的设计更合理,功能更强大。 - 旧系统和特定工具依赖: 如果你的系统较老,或者你使用的某些工具(比如某些监控代理)只支持v1,那么你可能不得不继续使用v1。但即使在v1环境下,了解v2的理念也很有帮助。
- 混合模式: 某些系统为了兼容性,可能同时挂载了v1和v2的cgroup文件系统。在这种情况下,你需要明确你正在操作的是哪个版本。
如何使用systemd管理cgroups资源限制?
说实话,手动在
/sys/fs/cgroup里敲命令,那真是有点原始,而且一旦重启就没了。systemd的出现,简直是把cgroups的配置从刀耕火种时代带入了工业革命。我个人现在基本都是通过systemd来搞定这些事,省心太多了。Systemd不仅管理服务,它也深度集成了cgroups,可以让你以声明式的方式为服务设置资源限制。
Systemd通过其Service、Scope、Slice等单元类型,将进程自动归类到cgroups中,并允许你直接在单元文件中定义资源限制。
基本原理: 当你启动一个systemd服务(
.service单元)时,systemd会自动为这个服务创建一个cgroup,并将服务的所有进程都放入这个cgroup中。然后,你可以通过在
.service文件中添加特定的配置项来设置资源限制。
配置示例(以一个名为 my-app.service
的服务为例):
创建或编辑服务文件:
sudo systemctl edit --full my-app.service
(如果文件不存在则创建,如果存在则编辑)-
添加或修改以下参数:
[Unit] Description=My Custom Application After=network.target [Service] ExecStart=/usr/local/bin/my_app_executable Restart=on-failure # 启用CPU和内存的会计功能(查看资源使用情况) CPUAccounting=true MemoryAccounting=true # CPU限制 # CPUQuota: cgroup v2 风格,限制CPU使用百分比 (20% CPU) CPUQuota=20% # CPUShares: cgroup v1 风格,相对CPU份额 (默认1024) # CPUShares=512 # 内存限制 # MemoryLimit: 内存使用上限 (512MB) MemoryLimit=512M # MemorySwapMax: 交换区使用上限 (0表示禁用交换区,防止进程使用过多交换区) MemorySwapMax=0 # I/O限制 (需要blkio控制器支持) # IOWeight: I/O相对权重 (默认1000) # IOWeight=500 # IOReadBandwidthMax=/dev/sda 10M # 限制从/dev/sda读取最大10MB/s # IOWriteBandwidthMax=/dev/sda 5M # 限制写入/dev/sda最大5MB/s [Install] WantedBy=multi-user.target
重新加载systemd配置并启动服务:
sudo systemctl daemon-reload
sudo systemctl start my-app.service
Systemd管理cgroups的优势:
-
声明式配置: 你在服务文件中声明你想要的资源限制,systemd负责底层cgroup的创建和管理。这比手动操作
/sys/fs/cgroup
目录下的文件要方便和安全得多。 - 持久性: 配置写入服务文件后,即使系统重启,资源限制也会自动生效。
- 集成度高: 与systemd的服务生命周期管理无缝集成,当服务启动、停止、重启时,cgroup也会被正确地创建、清理或重用。
-
易于监控: 通过
systemd-cgtop
命令可以方便地查看当前cgroup的资源使用情况,或者通过systemctl status my-app.service
查看服务的资源会计信息。 -
灵活性: 除了Service单元,Systemd还提供了
Slice
和Scope
单元来更灵活地组织和管理cgroups,例如,你可以创建一个Slice
来管理一组相关的服务,并为整个Slice
设置资源限制。
利用systemd来管理cgroups,是现代Linux系统上最推荐的做法,它不仅简化了操作,还提高了系统的可维护性和可靠性。









