ulimit 很高,服务仍报 Too many open files:查实际进程软限制
终端 ulimit 很高而服务报 Too many open files 时,读取实际 PID 软限制,按启动链设置 unit 或应用限制,并核对高编号支持与泄漏。
应用创建 socket 或打开文件时提示 Too many open files,终端运行 ulimit -n 却返回很高的值。应用如果在约 1024 个文件描述符附近失败,先读取报错进程自己的限制。终端和应用可能由不同启动链创建,两个值并不矛盾。
以下针对 Linux 的 RLIMIT_NOFILE,以及 systemd 管理的系统服务或用户服务。socket、管道等也占文件描述符,不能把限制理解为“最多打开多少个普通文件”。
用报错的 PID 读取软限制
先确认真正执行失败的进程 PID。systemd 服务可以读取 MainPID,但有 worker 的服务应继续找到报错 worker,不能只检查主进程。下面的 app.service 与 1234 是示例,PID 必须在实际运行环境中确认:
systemctl show app.service -p MainPID
cat /proc/1234/limits
查看 Max open files 的 Soft Limit 与 Hard Limit。软限制是当前执行上限,硬限制是普通进程可以把软限制提高到的边界。硬限制很高但软限制仍为 1024,应用就仍可能在这一范围附近失败。
限制更精确地约束可打开的描述符编号范围,标准输入输出、socket、管道和其他已打开对象都会占用编号。看到“1019 个 socket”不能因为小于 1024 就排除这项限制。
容器或不同 PID 命名空间内的 PID 不能直接套到宿主机的 /proc 路径。读取权限不足时通过授权诊断入口处理,不需要为了查看限制改变应用运行用户。保留真实错误、PID 和启动方式,不要只截取一份交互终端输出。
如果进程的软限制已经符合预期,继续检查文件描述符使用是否持续增长、失败是否来自另一个进程,以及错误的实际系统调用。打开对象泄漏与启动限制过低可以产生类似症状,不能只靠扩大数值判定原因。
systemd 启动的服务,在正确 unit 上设置
确认由系统级 systemd 服务启动,且应用确实能处理高编号描述符后,可以在这个服务的 drop-in 中设置 LimitNOFILE。先保存实际 unit 和现有覆盖配置,避免修改软件包提供的主文件或影响其他服务。
下面是服务覆盖片段,65535 仅是示例值,需要按应用容量和资源预算确定:
[Service]
LimitNOFILE=65535:65535
冒号两侧分别是软限制和硬限制;只写一个值则同时设置两者。提高软限制前要确认应用没有依赖 Linux select 的 1024 描述符边界;只改限制无法让使用这类接口的程序安全处理高编号对象。
将片段通过当前服务的正常配置入口保存后,需让 systemd 重新读取 unit,并在维护窗口重新创建服务进程。例如:
sudo systemctl daemon-reload
sudo systemctl restart app.service
app.service 要替换成已核对的系统服务名。重启会中断连接或当前任务,应先协调业务影响。daemon-reload 只更新管理器的配置理解,不会替正在运行的进程改变资源限制。
重新获取 PID,再读 /proc 下的限制。若仍是旧值,核对是否编辑了另一个 unit、是否存在其他覆盖、报错 worker 是否来自另一条启动链,以及程序启动后是否自行降低限制。不能仅凭文件里出现 LimitNOFILE 就报告生效。
用户服务受到用户管理器的硬限制约束
如果应用由 systemctl --user 管理,就应在用户服务范围查配置。系统服务 drop-in 不会自动作用于同名用户服务;两种管理器也不是在终端中运行一次 ulimit 就都会更新。
用户服务通常不能超过用户管理器启动时继承的硬限制。需要更高上限时,由管理员评估对应 PAM 或 user@.service 等配置,并协调重启用户管理器;这可能影响该用户的其他服务,不能照搬系统服务的单条重启命令。
修改 system.conf 或 user.conf 的 DefaultLimitNOFILE 属于默认配置调整,作用范围比单个服务大,也不是查看某个进程是否生效的替代办法。能在明确 unit 上解决时,不必为了一个应用先改变全机默认。
应用自行调整时也要检查返回结果
硬限制已经足够、应用具备相应能力时,也可由程序调用 getrlimit/setrlimit 选择合适软限制。普通进程能提高软限制到现有硬限制,不能无权限无限提高硬限制;调用后应检查错误并重新读取有效值,而不是忽略失败继续运行。
这属于应用启动行为变更,需要代码审查和隔离验证。不要把打印出的旧硬限制强制转为所有平台通用的固定整数,也不要为成功提升限制让整个服务改成 root。
验收应确认实际报错进程使用预期软限制,在业务批准的负载下不再出现同一错误,并观察打开对象数量和内存是否稳定。提高上限不会修复未关闭的描述符,持续泄漏仍需要修正应用。
回退时恢复本次修改的覆盖项或应用启动配置,并按业务窗口重新启动。只移除本次拥有的改动,不要用清空全部 drop-in 的办法误删其他服务设置;配置恢复也不会自动让已经运行的进程继承旧限制。


