最常见根源是 ulimit -u 限制被耗尽;线程也占 PID,多线程应用易触发;需检查软限制、用户线程总数、pid_max 全局值及 systemd/容器特异性配置。

查 ulimit -u 看用户级进程/线程总数是否打满
这个错误最常见根源就是 ulimit -u 限制被耗尽。Linux 中线程也占 PID,Java、Node.js、Python 多线程应用很容易在几十个进程里拉出几千个线程,直接撞上限。
执行 ulimit -u 查当前软限制;用 ps -u $USER -L | wc -l 统计该用户所有线程数(-L 表示含线程);两者接近就基本坐实了。
- 注意:不是看
ps -u $USER | wc -l—— 那只算进程,漏掉大量线程 - 临时验证:切到目标用户下运行
ulimit -u 65535 && your-app,如果不再报错,说明就是它 - 别只改 soft,hard 限也要同步调大,否则某些服务(如 systemd 启动的)会 fallback 到 hard 值
确认 /proc/sys/kernel/pid_max 是否过低
ulimit -u 是 per-user 限制,但系统全局 PID 池由 /proc/sys/kernel/pid_max 控制。如果这个值太小(比如默认 32768),即使每个用户没超限,全局 PID 耗尽后也会统一返回 Resource temporarily unavailable。
运行 cat /proc/sys/kernel/pid_max 查当前值;再用 ps -eLf | wc -l 看全系统线程总数。若后者接近前者,就得调大。
- 临时生效:
echo 131072 > /proc/sys/kernel/pid_max - 永久生效:在
/etc/sysctl.conf加kernel.pid_max = 131072,再运行sysctl -p - systemd 服务不读
limits.conf,必须显式在 service 文件里加LimitNPROC=才能突破ulimit -u
区分 fork 失败和非阻塞 I/O 的 EAGAIN
同样是 Resource temporarily unavailable,来源完全不同:fork 失败是资源池满(进程/线程/PID/内存页),而 socket 上的 recv/send 返回 EAGAIN 是数据未就绪——后者是正常行为,前者才是故障。
Zynq‑7000 / Zynq UltraScale+ 硬件调试技能。适用于 Xilinx Zynq(Cortex‑A9/A53 + FPGA)Linux 环境,调试 I2C、I2S、UART、GPIO 等外设。包括设备树配置、内核驱动、用户空间调试工具、交叉编译和常见问题排查,支持 PetaLinux 开发。
看上下文定位:
- 报错出现在启动时(
su、ssh、bash)、命令执行失败(ls: fork: retry...)、服务起不来 → 一定是资源限制 - 报错出现在网络收发日志里,且伴随
epoll_wait或循环重试逻辑 → 很可能是非阻塞 socket 的正常 EAGAIN - 用
strace -f -e trace=fork,clone,execve -p $PID 2>&1 | grep -i "eagain\|resource"可直接看到哪个系统调用卡住
systemd 服务绕过 limits.conf 的典型陷阱
很多团队改完 /etc/security/limits.d/*.conf,却发现 Java 服务还是报 unable to create new native thread —— 因为 systemd 默认不加载这些配置。
必须在对应 service 文件里显式声明:
- 在
[Service]段加LimitNPROC=65535(对应ulimit -u) - 加
TasksMax=65535(systemd 229+ 版本用,兼容性更好) - 改完记得
systemctl daemon-reload & systemctl restart your-service - 验证是否生效:
prlimit -n $(pgrep -f 'your-java-app'),看MAX列是不是你设的值
真正容易被忽略的是:容器环境(Docker/Podman)和 systemd 一样,也不继承 host 的 limits.conf,得靠 --ulimit 或 cgroup v2 配置单独设。

















