1
2
3
4
5
6
7
作者:李晓辉

联系方式:

1. 微信:Lxh_Chat

2. 邮箱:939958092@qq.com
  1. Linux 性能调优系列(一) 建立科学的性能分析思路
  2. Linux 性能调优系列(二) 掌握基础性能监控命令
  3. Linux 性能调优系列(三) sysstat实现性能数据持久采集
  4. Linux 性能调优系列(四) 别只会用 sysstat,PCP 才是 Linux 性能监控的进阶玩法
  5. Linux 性能调优系列(五) 给你的服务器做一次全面体检
  6. Linux 性能调优系列(六) 想调内核参数?先搞懂 /proc、sysctl 和 sysfs
  7. Linux 性能调优系列(七) 还在手动改 sysctl?TuneD 智能性能调优实战来了
  8. Linux 性能调优系列(八) 进程太能吃资源怎么办?用 ulimit 和 systemd 给它加个"上限"
  9. Linux 性能调优系列(九) cgroup v2实战:防止一个烂进程干崩整台服务器
  10. Linux 性能调优系列(十) CPU 100% 到底是谁在忙?用 perf 把"元凶"揪出来
  11. Linux 性能调优系列(十一) 程序为什么卡住了?strace 和 ltrace 带你找到答案
  12. Linux 性能调优系列(十二) 服务器又卡了?SystemTap 带你杀到内核看看为啥
  13. Linux 性能调优系列(十三) 从 SystemTap 到 eBPF,Linux 性能追踪进入新时代
  14. Linux 性能调优系列(十四) CPU 这么忙,到底是谁决定“让谁先跑”?一文搞懂 Linux 调度器
  15. Linux 性能调优系列(十五) CPU 到底该先给谁?搞懂 Linux 实时调度
  16. Linux 性能调优系列(十六) Linux CPU 隔离实战,为什么实时线程绑核后延迟还会抖?
  17. Linux 性能调优系列(十七) CPU 占用不高却跑的慢?深挖 CPU 缓存性能瓶颈
  18. Linux 性能调优系列(十八) 程序申请了1GB内存,为什么系统却说它没用?
  19. Linux 性能调优系列(十九) 服务器硬盘慢得像蜗牛?先别急着换硬盘,这四招先把瓶颈找出来
  20. Linux 性能调优系列(二十) 磁盘 I/O 调度器怎么选?一文搞懂 mq-deadline、kyber、bfq 和 none
  21. Linux 性能调优系列(二十一) 磁盘为什么慢?从文件系统到 I/O 调优,一次讲透
  22. Linux 性能调优系列(二十二) Linux 网络调优别乱改 sysctl!先找瓶颈再调 TCP 缓冲区
  23. Linux 性能调优系列(二十三) MTU 从 1500 到 9000,巨帧真的能提升性能吗?
  24. Linux 性能调优系列(二十四) 从 CPU 省电到网卡卸载,聊聊 Linux 网络调优
  25. Linux 性能调优系列(二十五) 聊聊VirtIO、TuneD、virtio-mem 调优

前面我们学习了内核可调项和 TuneD 调优工具,这些主要是在操作系统层面调整系统行为。但是实际生产环境还有一种很常见的情况。一台服务器上可能同时跑着很多业务,比如 Web 服务、数据库、监控程序、日志程序……

如果其中某一个程序突然出现异常,疯狂占用 CPU、创建大量进程、打开海量文件,甚至不断申请系统资源,那么它很可能会把其他业务一起拖垮。这时候我们就需要给进程或者服务设置一些资源上限。

你可以把它理解成:

服务器就像一栋办公楼,每个业务就是一个公司。

如果某家公司突然疯狂占用会议室、电话线路、办公桌,其他公司自然就没地方用了。

所以管理员会给它规定:

“你最多只能用这么多资源,超过这个范围就不允许继续申请。”

Linux 中的资源限制,就是在做类似的事情。


什么是 POSIX 资源限制?

在 Linux 中,一个进程能够使用多少系统资源,并不是完全没有限制的。系统可以通过 POSIX resource limits,也就是 POSIX 资源限制,对进程能够使用的部分资源设置上限。

例如:

  • 最多可以打开多少文件
  • 最多可以创建多少进程
  • 最多可以使用多少 CPU 时间
  • 栈空间最大是多少
  • core dump 最大是多少

Linux中常见的两种操作方式是:

方式主要作用对象典型用途
ulimit当前 Shell 及其子进程临时测试、用户登录会话
systemd Limitsystemd 管理的服务进程给后台服务设置资源限制

这里大家先记住一个非常重要的区别:

ulimit 更偏向“用户登录会话”;systemd 更适合“后台服务”。

比如:

你 SSH 登录服务器之后,在命令行里运行一个程序,这时候可以使用 ulimit。但是如果是 Nginx、MySQL、Redis 这种由 systemd 管理的后台服务,就应该考虑在对应的 systemd service 中设置限制。而如果以后需要对 CPU、内存等资源进行更加完整的服务级隔离,就会涉及 cgroup v2,这个我们后面再讲。


ulimit 命令详解

ulimit 是 Bash 的内置命令,用于查看或者修改当前 Shell 的资源限制。

这里一定要注意:

ulimit 修改的是当前 Shell 进程的资源限制。

而 Shell 启动出来的子进程,会继承这些限制。

所以可以简单理解成:

1
2
3
4
5
当前 Shell
│
├── 子进程 A
├── 子进程 B
└── 子进程 C

如果我们在这个 Shell 中设置了资源限制,那么从这个 Shell 启动的子进程通常也会继承相应限制。

这也是为什么:

直接在终端执行 ulimit,通常只影响当前会话,不会影响其他 SSH 会话。


soft limit 和 hard limit

ulimit 中有两个非常重要的概念:

soft limit:软限制

软限制可以理解成:

当前真正生效的限制值。

普通用户可以在权限允许的范围内调整自己的 soft limit,但不能超过 hard limit。

hard limit:硬限制

hard limit 可以理解成:

这个资源允许设置的最高上限。

普通用户不能把自己的 hard limit 随便调高。

所以可以简单记成:

1
2
3
4
5
6
7
hard limit
↓
允许达到的最高上限

soft limit
↓
当前实际使用的限制

例如:

1
2
soft = 100
hard = 200

那么当前实际限制是 100。用户可以把 soft 从 100 调到 150,但不能调到 300,因为已经超过 hard limit。

不同资源达到限制之后的具体行为并不完全一样,所以不要简单理解成“所有限制达到以后都会被杀掉”,也就是说**ulimit 的不同资源限制,达到上限后,程序的反应是不一样的,并不是所有情况都会直接把进程杀掉。**。

资源参数达到限制后的典型行为
CPU 时间-t / LimitCPU超过限制后,进程可能收到 SIGXCPU,继续超限可能被终止
打开文件数-n / LimitNOFILE不会直接杀进程,程序继续运行,但再打开文件可能失败
用户进程数-u / LimitNPROC创建新进程/线程可能失败,已有进程通常不会因此被杀掉
Core 文件大小-c / LimitCORECore Dump 文件可能被截断或无法生成,并不是杀掉原进程
文件大小-f / LimitFSIZE继续写文件可能失败,进程不一定被直接杀掉

举个最容易理解的例子:nofile,假设:

1
ulimit -n 100

意思是:

当前进程最多可以同时打开 100 个文件描述符。

假设一个程序已经打开了 100 个文件,这时候它又想打开第 101 个:

1
2
3
4
5
6
7
8
9
打开第 101 个文件
↓
超过 NOFILE 限制
↓
open() 调用失败
↓
程序收到“打开失败”的错误
↓
程序是否退出,由程序自己的错误处理决定

并不是 systemd 或内核看到达到 100,就直接把这个进程杀掉。

例如程序处理得比较好:

1
2
3
4
5
6
7
打开文件失败
↓
记录日志
↓
稍后重试
↓
程序继续运行

处理得不好,也可能:

1
2
3
4
5
打开文件失败
↓
程序没有正确处理异常
↓
程序自己退出

所以这里真正需要理解的是:

资源限制更像是在资源入口处设置了一道“门槛”,超过门槛以后,具体发生什么,要看这个资源的语义以及应用程序如何处理失败。

那真正需要限制内存怎么办?

如果你的场景是:

“服务器上运行一个 systemd 服务,我不希望它把整台机器的内存吃光。”

这时候应该使用 systemd 的:

1
MemoryMax=

例如:

1
2
[Service]
MemoryMax=1G

可以把它理解成:

1
2
3
4
5
6
7
8
9
10
11
服务器 16 GB RAM
│
├── MySQL
│
├── Nginx
│
└── example.service
│
└── MemoryMax=1G
↓
给这个服务设置内存上限

systemd 会利用 cgroup 对服务进行资源控制,至于这个,我下面会介绍,这里先理解就行。


交互式操作 ulimit

先来看当前 Shell 的资源限制。

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@localhost ~]# ulimit -a
real-time non-blocking time (microseconds, -R) unlimited
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 30400
max locked memory (kbytes, -l) 8192
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 30400
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

可以查看当前 Shell 的各种资源限制。如果想明确查看 soft limit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@localhost ~]# ulimit -S -a
real-time non-blocking time (microseconds, -R) unlimited
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 30400
max locked memory (kbytes, -l) 8192
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 30400
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

如果想查看 hard limit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@localhost ~]# ulimit -H -a
real-time non-blocking time (microseconds, -R) unlimited
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 30400
max locked memory (kbytes, -l) 8192
max memory size (kbytes, -m) unlimited
open files (-n) 524288
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) unlimited
cpu time (seconds, -t) unlimited
max user processes (-u) 30400
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

其中:

1
-S

表示 soft limit。

1
-H

表示 hard limit。

如果设置的时候不指定 -S 或 -H,需要特别注意命令的具体行为和当前权限,不要想当然地认为它只修改 soft limit。

你可能会发现,很多参数的输出完全一样。这并不代表 Soft 和 Hard 没有区别,而是因为这些资源当前设置的 Soft 和 Hard 值刚好相同。真正有区别的地方,可以看下面几个例子:

  • open files(-n)

    • Soft:1024
    • Hard:524288
    • 说明:当前 Shell 最多打开 1024 个文件,但 Soft limit 最多可以提高到 524288。
  • stack size(-s)

    • Soft:8192 KB
    • Hard:unlimited
    • 说明:当前限制是 8192 KB,但 Hard limit 没有设置上限。
  • pending signals(-i)

    • Soft:30400
    • Hard:30400
    • 说明:Soft 和 Hard 当前设置成了一样的值,所以两边显示相同。
  • CPU time(-t)

    • Soft:unlimited
    • Hard:unlimited
    • 说明:Soft 和 Hard 都没有设置 CPU 时间限制,所以两边显示相同。

因此大家可以简单理解:

  • Soft limit:当前真正生效的资源限制。
  • Hard limit:Soft limit 能够提升到的最高边界。
  • 如果 Soft 和 Hard 本身设置成一样,执行 ulimit -S -a 和 ulimit -H -a 看起来就会完全一样。
  • 如果两者不同,就能明显看到区别。

例如:

1
2
3
4
open files (-n)

Soft:1024
Hard:524288

这就意味着:

现在只能打开 1024 个文件,但可以把 Soft limit 调高,最高不能超过 524288。


实操案例:查看单项资源限制

比如我们想看看:

当前 Shell 允许进程累计使用多少 CPU 时间。

可以执行:

1
2
[root@localhost ~]# ulimit -S -t
unlimited

这里的:

1
unlimited

表示当前没有设置 CPU 时间上限。

为什么重新打开 SSH 窗口就失效?

如果我们刚才执行的是:

1
ulimit -S -t 60

它修改的是:

当前 Shell 的资源限制。

比如:

1
2
3
4
5
6
7
8
9
SSH窗口 A
│
└── Shell A
└── 测试程序

SSH窗口 B
│
└── Shell B
└── 测试程序

我们只在 Shell A 中执行了:

1
ulimit -S -t 60

那么 Shell B 并不会自动继承这个限制。

所以:

直接执行 ulimit,非常适合临时测试,但不适合作为长期配置方案。


ulimit 如何持久化?

刚才我们直接执行:

1
ulimit -S -t 60

退出当前 Shell 后,这个配置就没有了。

如果希望:

用户每次登录服务器时,都自动获得指定的资源限制。

那么就需要使用 PAM 的 pam_limits 机制。Linux中常见的配置文件是:

1
2
/etc/security/limits.conf
/etc/security/limits.d/*.conf

其中更推荐把自己的配置放在:

1
/etc/security/limits.d/

这样做的好处是:

不需要直接修改系统主配置文件,自己的配置更加独立,也方便后期维护。


limits.conf 配置格式

配置格式是:

1
<domain> <type> <item> <value>

分别表示:

参数含义
domain对哪个用户或用户组生效
typesoft 或 hard
item要限制的资源
value限制值

例如:

1
@managers hard maxlogins 3

假设服务器上有一个managers用户组。现在公司的要求是:

managers 组中的用户最多同时建立 3 个登录会话。

我们可以创建:

1
/etc/security/limits.d/managers.conf

内容:

1
@managers hard maxlogins 3

这样,当属于这个组的用户进行 PAM 登录时,就会受到这个限制。

可以把它理解成:

公司办公楼规定:这个部门最多只能同时占用 3 个工位。

第 4 个登录请求就会受到限制。


一个非常重要的坑:limits.conf 不管 systemd 服务

这里是这篇文章非常重要的一个知识点。很多初学者会有这样的想法:

“我把 /etc/security/limits.d/ 里面的 nofile 调大了,那 Nginx、MySQL 应该也跟着变大了吧?”

不一定,而且对于 systemd 直接启动的后台服务,通常不是这么生效的。

原因很简单:

1
2
3
4
5
6
7
SSH登录用户
↓
PAM
↓
pam_limits
↓
limits.conf / limits.d

而 systemd 启动后台服务,并不是通过这种 PAM 登录会话启动的。

所以:

1
2
/etc/security/limits.conf
/etc/security/limits.d/*.conf

主要针对 PAM 登录会话。而 systemd 管理的后台服务,要从 systemd 自己的配置入手。


使用 systemd 给服务设置资源限制

systemd 不仅负责:

1
2
3
启动服务
停止服务
重启服务

它还可以参与:

1
资源管理

现在大家对systemd 的资源管理推荐程度也比较高,其资源控制底层会结合 cgroup 等机制管理服务进程。所以,如果我们的需求是:

“我要限制一个后台服务。”

那么相比修改用户的 limits.conf,更应该考虑 systemd 的服务级配置。


systemd 的 Limit* 参数

在 systemd service 的[Service]部分,可以使用很多Limit参数。

这些参数对应 Linux 的 POSIX resource limit,也就是我们前面讲的 ulimit / setrlimit 这一套限制机制。

常见的对应关系如下:

systemd 参数ulimit 选项作用
LimitCPU=-tCPU 时间限制
LimitNOFILE=-n最大打开文件描述符数量
LimitNPROC=-u用户进程数量限制
LimitSTACK=-s进程栈大小
LimitCORE=-ccore dump 文件大小

systemd 修改配置的正确姿势

这里还有一个生产环境非常重要的习惯。不要直接修改软件包安装的:

1
/usr/lib/systemd/system/xxx.service

因为软件升级的时候,这些文件可能被软件包更新。更推荐使用:

1
/etc/systemd/system/<服务名>.service.d/

创建 drop-in 配置片段。

这样可以:

不修改原始 service 文件,只额外增加自己的配置。

而 /etc/systemd/system/ 的优先级高于 /usr/lib/systemd/system/。

实操案例:给 sshd 服务设置 100M 内存上限

假设现在我们希望限制 sshd 服务:

sshd 服务及其 cgroup 中的进程,内存使用量最多为 100MB。

首先创建 sshd 的 drop-in 配置目录:

1
[root@localhost ~]# mkdir -p /etc/systemd/system/sshd.service.d

然后创建配置文件:

1
2
3
4
cat > /etc/systemd/system/sshd.service.d/10-memorylimits.conf <<EOF
[Service]
MemoryMax=100M
EOF

配置完成后,让 systemd 重新读取配置:

1
systemctl daemon-reload

然后重启 sshd 服务:

1
systemctl restart sshd

这样,新的 sshd 服务进程就会受到 MemoryMax=100M 的内存限制。

查看配置是否生效可以使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@localhost ~]# systemctl cat sshd
# /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.target
Wants=sshd-keygen.target
# Migration for Fedora 38 change to remove group ownership for standard host keys
# See https://fedoraproject.org/wiki/Changes/SSHKeySignSuidBit
Wants=ssh-host-keys-migration.service

[Service]
Type=notify
EnvironmentFile=-/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

# /etc/systemd/system/sshd.service.d/10-memorylimits.conf
[Service]
MemoryMax=100M

或者

[root@localhost ~]# systemctl show sshd -p MemoryMax
MemoryMax=104857600

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

说明 `100M` 的内存限制已经应用。也可以查看服务当前的 cgroup 资源使用情况:

```bash
[root@localhost ~]# systemctl status sshd
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; preset: enabled)
Drop-In: /etc/systemd/system/sshd.service.d
└─10-memorylimits.conf
Active: active (running) since Wed 2026-09-30 12:52:44 CST; 1min 2s ago
Invocation: 202719eef634447b9ca9afb7b64c8c4b
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 10745 (sshd)
Tasks: 1 (limit: 48640)
Memory: 1.2M (max: 100M, available: 98.7M, peak: 1.3M)
CPU: 7ms
CGroup: /system.slice/sshd.service
└─10745 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"

⚠️ 实际生产环境要特别注意

这里大家不要简单理解成:

“sshd 自己最多只能使用 100MB。”

更准确地说:

MemoryMax=100M 限制的是 sshd.service 对应 cgroup 的内存使用上限。

如果该 cgroup 中的内存使用超过限制,systemd/cgroup 会采取内存回收和 OOM 处理机制,相关进程可能被终止。

因此,生产环境中不要随便给 sshd 设置一个过低的 MemoryMax。否则可能导致 SSH 服务异常,甚至影响远程登录和运维。

这个案例主要是为了帮助我们理解:

1
2
3
4
5
6
7
8
9
systemd
↓
sshd.service
↓
cgroup
↓
MemoryMax=100M
↓
限制该服务的内存使用

这也是现代 Linux 中使用 systemd 管理服务资源时比较典型的思路,具体的值大家还是要理性设置,不要让我老李背锅。


为什么需要 daemon-reload?很多刚接触 systemd 的同学会问:

“我都把文件改好了,为什么还要 daemon-reload?”

原因是:

systemd 本身需要重新读取 unit 配置。

所以我们修改 service 配置后,一般按照这个流程:

1
2
3
4
5
6
7
修改配置文件
↓
systemctl daemon-reload
↓
systemctl restart XXX.service
↓
新的服务进程加载新配置

注意:

1
daemon-reload

只是让 systemd 重新读取配置。

它不会自动把已经运行的服务进程全部按照新的限制重新启动。

因此这里还需要:

1
systemctl restart xxx.service

ulimit 和 systemd Limit* 到底怎么选?

到这里,我们把前面的内容串起来。

方式主要生效对象配置方式适合场景
ulimit当前 Shell 及其子进程命令行临时测试
pam_limitsPAM 登录会话limits.conf / limits.dSSH 等用户登录会话
systemd Limit*systemd 服务的进程service / drop-in后台服务的 POSIX 资源限制
systemd cgroupservice 对应的资源组MemoryMax、CPUQuota、TasksMax 等更完整的服务级资源控制

这里特别注意:

systemd 的资源管理并不只有 Limit*。

例如真正要控制一个服务:

1
2
3
最多使用多少内存
最多使用多少 CPU
最多创建多少任务

生产环境应该怎么做?

实际生产环境中,不建议一上来就给所有服务设置一大堆限制。更合理的思路是:

第一步:先明确问题

例如发现:

1
Too many open files

再去研究:

1
nofile

而不是:

“网上有人说 Linux 性能调优必须把所有参数都调大,那我也全部调大。”


第二步:根据进程启动方式选择方案

如果是:

1
2
3
SSH登录
↓
用户运行程序

可以考虑:

1
2
3
ulimit
limits.conf
limits.d

如果是:

1
2
3
systemd
↓
nginx.service

那么优先考虑:

1
systemd service

对应的:

1
Limit*

或者后续学习的:

1
cgroup

第三步:先临时测试,再持久化

这个思路和前面的内核参数调优是一致的。先测试:

1
ulimit

或者临时修改 service 配置。

确认业务没有异常以后,再正式写入:

1
/etc/security/limits.d/

或者:

1
/etc/systemd/system/<service>.service.d/

第四步:修改后一定验证

比如修改 systemd 配置后,可以检查:

1
systemctl show example.service | grep '^Limit'

也可以针对具体参数查看:

1
systemctl show example.service -p LimitNOFILE

确认 systemd 当前真正加载的值,而不是只看自己写入的配置文件。


最后总结一下

这一篇我们主要解决一个问题:

如果某个进程或者服务太能“吃资源”,怎么给它设置一个边界?

先记住三个东西:

1
2
3
ulimit
limits.conf
systemd Limit*

它们分别解决不同场景。

场景优先考虑
临时测试当前 Shellulimit
SSH 用户登录会话limits.conf / limits.d
systemd 后台服务systemd Limit*
服务级 CPU、内存、任务数等资源控制systemd + cgroup

还有一个非常重要的经验:

不要看到资源限制就全部设置成一个很大的数字。

例如:

1
2
LimitNOFILE=999999
LimitNPROC=999999

并不代表性能一定更好。资源限制本质上是在:

系统稳定性、业务需求和资源利用率之间找一个合适的边界。

真正做生产调优时,应该先知道:

1
2
3
4
5
6
7
8
9
10
11
现在出了什么问题?
↓
哪个资源成为瓶颈?
↓
是谁在消耗资源?
↓
需要限制什么?
↓
应该使用哪一种限制机制?
↓
修改后验证业务表现

而不是:

1
2
3
4
5
复制一堆参数
↓
全部调大
↓
祈祷服务器性能变好

参考手册

1
2
3
4
5
6
7
8
man bash
man ulimit
man limits.conf
man pam_limits
man setrlimit
man systemctl
man systemd.exec
man systemd.resource-control

下一篇预告:如果只是使用 ulimit 和 Limit*,我们解决的主要还是传统 POSIX resource limits。真正想做到**“这个服务最多用多少 CPU、多少内存、最多创建多少任务”**,就需要进一步学习 Linux 的 cgroup v2,以及 systemd 如何通过 cgroup 对服务进行资源控制。