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

systemd 服务里的 LimitNOFILE 为什么和 shell ulimit 不同

来源:17golang原创

时间:2026-10-07 00:08:22 258浏览 收藏

同一台 Linux 主机上,在终端执行 ulimit -n 得到 1024,服务进程却可能是 65536;这通常不是 systemd 失效,而是两个命令观察的进程上下文不同。shell 的 ulimit 改变当前 shell 的资源限制,并由它启动的子进程继承;systemd 服务由 manager 按单元配置启动,读取的是 manager 能提供的限制和 LimitNOFILE= 设置。

要点速览
  • LimitNOFILE= 设置服务进程的文件描述符 soft/hard limit,单值表示两者相同,soft:hard 可以分别设置。
  • 不要用当前终端的 ulimit -n 代替服务验证;优先查看 systemctl show、/proc/PID/limits 和 /proc/PID/fd。
  • 修改 drop-in 后必须 daemon-reload 并重启服务,且旧程序若使用 select,不要盲目把 soft limit 提到 1024 以上。
systemd LimitNOFILE 与 shell ulimit 的启动上下文和 soft hard limit 关系说明图
图1:启动上下文说明图,展示 shell ulimit 与 systemd LimitNOFILE 的作用域差异。
systemd 服务的 LimitNOFILE 和 shell 里跑 ulimit 看到的值不一样,核心原因是两套限制的生效层级、继承逻辑和优先级规则完全不同,系统启动时的硬限制并不会直接透传给后续 systemd 拉起的服务进程,很多人踩坑就是没理清几者的覆盖关系。

LimitNOFILE 和 shell ulimit 处在两条启动上下文里

ulimit -n 是 shell 内建命令,它显示或修改当前 shell 的文件描述符限制。你在终端里执行它,只能直接说明这个终端及其后代进程的限制;已经由 PID 1 的 systemd manager 启动的服务,并不会回头读取你后来在终端里改过的值。

LimitNOFILE= 属于 systemd 执行环境配置,最终通过 Linux 的进程资源限制生效。配置写成单个数字时,soft 和 hard 使用同一个值;写成 65536:131072 时,服务可先使用 65536,进程在具备相应权限和实现支持时再把 soft limit 提高到 hard limit。这里的“文件”也包括 socket、管道等文件描述符,不只是磁盘文件。

先读取服务自己的限制,不要猜 manager 的默认值

排查时先取得主进程 PID,再同时看单元解析结果和进程内核视图。下面的命令只展示检查路径,输出中的数值应以你的服务为准。

# 替换为实际单元名,先查看 systemd 解析后的限制
systemctl show myapp.service -p MainPID -p LimitNOFILE

# 读取服务主进程的 soft/hard limit;PID=0 说明服务没有运行
pid="$(systemctl show -p MainPID --value myapp.service)"
if [ "$pid" -gt 0 ]; then
  awk '/Max open files/ {print}' "/proc/$pid/limits"
  printf '当前 fd 数量:'
  find "/proc/$pid/fd" -mindepth 1 -maxdepth 1 -type l | wc -l
fi

systemctl show回答“单元当前解析到什么”,/proc/PID/limits回答“这个服务进程真正拿到了什么”,而 /proc/PID/fd 的数量只是当前使用量。使用量接近 soft limit 时,才更像是 fd 耗尽;如果限制值不对,继续加业务重试没有意义。

观察位置它说明什么不能据此推出什么
终端 ulimit -n当前 shell 的 soft limit不能代表 systemd 服务
systemctl show单元和 manager 的解析结果不等于某个旧进程已经重启
/proc/PID/limits目标进程实际 soft/hard limit不等于当前已打开 fd 数
/proc/PID/fd进程当前 fd 使用量不说明限制来自哪一层

用 drop-in 明确设置并完成一次重启

生产环境更适合给单元增加 drop-in,而不是直接改发行版提供的主 unit 文件。下面的示例把服务 soft limit 设为 65536、hard limit 设为 131072;数值要按应用是否使用高 fd、内核资源和旧 API 兼容性评估。

# 创建本地 drop-in,避免覆盖软件包提供的 unit 文件
sudo systemctl edit myapp.service

# 在编辑器中写入以下 [Service] 配置
[Service]
LimitNOFILE=65536:131072

# 让 manager 重新读取 unit,并重启服务创建新进程
sudo systemctl daemon-reload
sudo systemctl restart myapp.service

# 重启后重新读取服务进程的实际限制
systemctl show myapp.service -p MainPID -p LimitNOFILE

如果只执行了 daemon-reload,它只刷新配置,不会改变已经运行的服务进程;如果只重启而没有刷新,可能继续使用旧解析结果。改完后再次读取 /proc/$pid/limits,这是判断是否真正生效的关键。

常见边界:继承、权限和旧接口

systemd system instance 的服务限制通常可以独立设置,但 user service 受到 user manager 启动时已有的上限约束,单元配置不能凭空把 hard limit 抬得比 manager 能提供的更高。此时要追查 user manager 的启动环境、PAM limits 或承载它的 system service,而不是反复修改服务文件。

还要区分“能打开多少 fd”和“程序是否能处理高编号 fd”。systemd 文档特别提醒,Linux 上传统 select 不能处理编号高于 1023 的 fd;使用 poll、epoll 或其他适配实现的程序,才适合评估更高的 soft limit。提升限制不是性能开关,也不能代替 cgroup 的内存、任务数和连接池配置。

systemd 服务 LimitNOFILE 配置来源与 proc 运行时证据的排查结构图
图2:排查结构图,把单元配置与服务进程实际限制、fd 使用量对应起来。

相关问题

为什么修改 /etc/security/limits.conf 后服务仍不变?

该文件主要通过 PAM 影响登录会话;systemd system service 不一定经过你的交互登录 PAM 链路。服务应优先在 unit 或 manager 层明确配置,再从服务 PID 验证。

LimitNOFILE=65536 为什么仍然打开失败?

可能是 soft limit、应用内部连接池、权限、内存压力或其他内核资源先到边界。先看 /proc/PID/limits 与实际 fd 数量,再定位应用报错。

改完配置后看哪个值最可信?

以重启后目标服务 PID 的 /proc/PID/limits 为准;systemctl show用于解释配置来源,终端 ulimit只作当前 shell 的参考。

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