如何正确fork并处理进程以避免EAGAIN错误(errno=11)
问题:fork大量短生命周期进程后出现errno=11错误
我需要fork大量短生命周期进程(无需等待其完成,只需让它自行运行)。运行一段时间后,执行fork时出现错误,errno等于11,且通过htop可以看到此前fork出的进程仍然存在。运行环境为Ubuntu 22.04。
示例代码
#include <cstdio> #include <cstdlib> #include <unistd.h> #include <errno.h> #include <signal.h> void RunProc(){ pid_t pid = fork(); if(pid == 0) { puts("fork with success"); //no needed to reproduce issue. Used in my code to run some commands. //execvp() exit(0); } else if (pid == -1) { printf("Could not start process\n"); printf("Errno: %d\n", errno); } else { } } int main() { while(true){ RunProc(); } int res = 0; int i = 5; res = scanf("%d", &i); res = printf("End of program %d\n", i); return res; }
错误输出
Could not start process
Errno: 11
解决方案
问题根源
errno=11对应EAGAIN,表示系统暂时无法分配足够资源创建新进程。核心原因是父进程未处理子进程的退出状态,导致子进程退出后变成僵尸进程(zombie process)。僵尸进程会持续占用系统进程表条目,当进程表被耗尽时,就无法再fork新进程,htop中看到的“仍存在”的进程就是这些僵尸进程。
修复方案1:忽略SIGCHLD信号(推荐)
如果不需要获取子进程的退出状态,直接告诉内核自动回收子进程是最简单的方式。在main函数开头添加以下代码:
signal(SIGCHLD, SIG_IGN);
内核会在子进程退出时自动清理其资源,不会产生僵尸进程。
修复方案2:主动回收僵尸进程
如果后续需要处理子进程的退出状态,可以通过waitpid非阻塞地回收已退出的子进程。修改RunProc函数:
void RunProc(){ pid_t pid = fork(); if(pid == 0) { puts("fork with success"); //execvp() exit(0); } else if (pid == -1) { printf("Could not start process\n"); printf("Errno: %d\n", errno); } else { // 非阻塞回收所有已退出的子进程 while (waitpid(-1, NULL, WNOHANG) > 0); } }
也可以注册SIGCHLD信号处理函数,在信号回调中调用wait或waitpid回收僵尸进程。
验证修复
修改代码后重新运行,使用ps aux | grep 'Z'查看是否还有僵尸进程。如果僵尸进程不再积累,说明修复生效,fork可以正常创建新进程。
内容的提问来源于stack exchange,提问作者AdamF
相关产品推荐
相关产品推荐

