登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

systemd TasksMax 为什么会把线程也计入上限

来源:17golang原创

时间:2026-11-10 08:16:47 252浏览 收藏

systemd 的 TasksMax 限制的是服务 cgroup 中的任务数量,线程也单独计数。一个只有少量进程的服务,如果每个进程创建很多线程,仍然可能耗尽任务预算。原因在于它使用 pids 控制器,而该控制器按内核任务标识的口径计数。

官方文档:https://www.freedesktop.org/software/systemd/man/latest/systemd.resource-control.html

先把进程数和任务数分开

面对“进程明明不多,却无法创建线程”的现象,我会先看任务数量,再考虑是否需要加限额。只数 ps 列出的进程行数,很容易漏掉线程池、运行时辅助线程以及服务启动的子进程。

例如一个主进程带有多个工作线程,预算不是只扣一份。服务 cgroup 及其后代中的任务共同占用相应计数。这里讨论 systemd 管理、使用 cgroup v2 的 Linux 服务,不把用户级进程限制与服务任务预算混成同一个开关。

服务进程工作线程子进程与任务计数关系
静态组成说明图:服务 cgroup 中的主线程、工作线程及子进程任务都属于任务计数范围;一个进程不等于一项预算。

读取服务所属 cgroup 的任务预算

先读取管理器中的配置与路径,再查看对应内核接口。以下命令供在目标 Linux 主机按需使用,替换 worker.service 为实际服务名。本文未执行命令,也不提供虚构线程数或日志。

# 读取服务任务配置、当前任务数和所属层级
systemctl show worker.service -p TasksCurrent -p TasksMax -p Slice -p ControlGroup
# 使用管理器返回的真实路径,不按进程名猜测 cgroup 位置
task_cgroup=$(systemctl show worker.service -p ControlGroup --value)
if [ -n "$task_cgroup" ]; then
  # 读取当前任务计数和本级上限;不存在时先确认 cgroup v2 与控制器
  cat "/sys/fs/cgroup$task_cgroup/pids.current"
  cat "/sys/fs/cgroup$task_cgroup/pids.max"
  # 事件值是累计记录,需结合故障窗口比较变化
  cat "/sys/fs/cgroup$task_cgroup/pids.events"
fi

把这几项记录在同一故障窗口:服务当前任务数、本级上限、父级配置和应用报错。不要用刚重启后的低计数来否定之前的耗尽,也不要拿累计事件值单独证明故障仍在持续。

给线程池制定有余量的限额

限额应覆盖正常峰值及启动、重载时的临时任务,同时约束异常扩张。我更倾向于先限制业务线程池和子进程并发,再按观察到的峰值设置服务预算,而不是直接去掉所有边界。

# 示例值不是推荐容量;先按正常线程和子进程峰值确定预算
sudo systemctl set-property --runtime worker.service TasksMax=640
# 读取当前配置,确认操作对象及返回值
systemctl show worker.service -p TasksMax -p TasksCurrent

--runtime 表示运行时设置,不作为跨重启保留的配置。若确认要长期使用,可在服务 drop-in 中保存对应配置,并按项目变更流程加载。先记录旧值,便于恢复;不要在多个位置留下含义冲突的设置。

# worker.service 的资源控制配置片段
[Service]
# 按本服务的正常任务峰值及余量设置,不等于允许 640 个独立进程
TasksMax=640

增加 TasksMax 不会替代内存预算。线程本身也需要资源,盲目放大线程池可能只是把“创建失败”变成内存压力或调度开销。修正并发模型与调整资源边界应分别说明目的。

结合父级和失败记录判断原因

服务上方的 slice 可能还有更紧的 pids.max。子服务当前计数看似有空间,也可能因为祖先已接近预算而无法创建新任务。因此,需要沿真实 cgroup 层级查看父级,而不是只调高服务自己的 TasksMax。

父级 slice 预算与服务 TasksMax 和事件记录关系
静态依赖说明图:服务限额、父级 slice 预算及计数记录需要一起判断,调高叶子配置不能覆盖祖先限制。
观察下一步判断
本级计数接近上限且事件增加分析线程池与子进程峰值
本级有余量但创建仍失败检查祖先预算及其他资源限制
任务数持续增长不回落检查应用生命周期和并发释放
调整后只短暂恢复不要继续盲目加额,查清增长来源

pids 控制器限制新任务创建,但降低限额不会自动杀掉已有线程。因此,看到当前数量高于配置也不一定是控制器失效。应用报错仍应结合其运行时与系统资源情况解释,不能把所有线程创建失败都归因于这一项。

TasksMax=100 就是最多 100 个进程吗?

不是。线程会计入预算,而且相关子进程也会贡献任务数。应按任务口径估算。

把线程池最大值设为同一个数就够了吗?

不够。还要考虑主线程、辅助线程、子进程以及阶段性峰值,两个数字的对象范围不同。

调大后还要看什么?

观察相同业务负载下任务数量是否稳定、应用创建失败是否消失,以及内存和响应是否符合目标。不要只看配置写入成功。

内核接口资料:https://docs.kernel.org/admin-guide/cgroup-v2.html。把线程计入预算是资源边界的一部分;真正需要修正的是进程行数与任务数之间的估算差异。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>