进程数持续增长不等于泄漏,需排查僵尸进程、pid_max上限及内核告警;重点检查定时任务失控、子进程未wait、服务反复重启;区分用户态(未wait)、内核态(线程未释放)及cgroup配置问题。

先看是不是真泄漏
进程数持续增长不等于一定存在泄漏。Linux 系统中,短生命周期进程(如 cron 任务、日志轮转脚本、HTTP 探针)频繁启停,ps aux | wc -l 数值波动是正常现象。关键要看:
– ps -eo stat,pid,comm --sort=pid | grep "^Z" 是否存在大量僵尸进程(Z 状态),说明父进程未回收;
– cat /proc/sys/kernel/pid_max 对比当前进程总数(ps -eL | wc -l),若接近上限(默认 32768),才真正有风险;
– dmesg -T | grep -i "out of memory\|pid_max" 查是否有内核级告警。
快速定位异常进程来源
重点检查三类高发场景:
– 定时任务失控:运行 systemctl list-timers --all 和 crontab -l(含 root 及各服务用户),确认是否存在未加锁、无超时控制的循环脚本;
– 子进程未 wait:对可疑服务(如自研守护进程、Python 后台任务),用 strace -p PID -e trace=clone,fork,vfork,wait4 2>&1 | grep -E "(clone|wait)" 观察是否调用 wait 类系统调用;
– 容器或 systemd 服务反复重启:查 journalctl -u your-service --since "2 hours ago" | grep -i "exited\|started\|failed",若 restart=always 但退出码非 0,可能陷入“启动→崩溃→重启”死循环。
区分用户态与内核态根源
进程创建本身消耗内核资源(task_struct、PID 描述符等),泄漏可能发生在不同层级:
– 用户态泄漏:程序 fork() 后未 waitpid(),或使用 popen() / subprocess.Popen() 启动子进程却忽略清理;常见于 Python/Shell 脚本中缺少 try/finally 或 signal handler;
– 内核态泄漏:极少见,但若 /proc/sys/kernel/pid_max 未达上限,而 cat /proc/sys/kernel/threads-max 与 ps -eL | wc -l 差值持续收窄,需怀疑 kernel thread 创建未释放(如异常驱动模块);
– tmpfs 或 cgroup 限制误配:在容器环境中,cat /sys/fs/cgroup/pids.max 若为小数值(如 100),而应用实际需更多进程,会导致 fork 失败并重试,表面看像泄漏,实为配置瓶颈。
验证与收敛手段
确认泄漏后,优先用低侵入方式收敛:
– 对 shell 脚本,在关键 fork 前加 set -o pipefail; trap 'kill $(jobs -p) 2>/dev/null' EXIT;
– 对 Python 进程,确保 subprocess 使用 subprocess.run(..., timeout=30) 并捕获异常;
– 临时限制进程数:prlimit --nproc=500 --pid PID(对单进程)或修改 systemd service 的 TasksMax=500;
– 清理僵尸进程:kill -s SIGCHLD $(ps -o pid= -C your-parent-binary)(仅对支持该信号的父进程有效),更可靠的是升级或修复父进程逻辑。









