Linux多线程环境下C程序调用system()报No Child process如何解决
问题根因
多线程环境下调用system()返回-1、errno为10(No child processes),核心触发原因是进程级的SIGCHLD信号处理逻辑破坏了system()的内部执行流程:
POSIX标准下system()的执行逻辑为:
- 调用
fork()创建子进程,在子进程中通过exec()加载shell执行传入的系统命令 - 父进程调用
waitpid()阻塞等待自身创建的子进程退出,获取命令执行的返回状态
当waitpid()找不到需要等待的目标子进程时,就会返回ECHILD(错误码10),对应报错"No child processes"。
结合程序中使用了sigaction、SIGQUIT的信息,绝大多数情况是以下原因导致:
- 通过
sigaction()自定义了SIGCHLD信号处理函数,在处理函数中调用waitpid(-1, &status, WNOHANG)无差别回收了所有已退出的子进程,包括system()内部创建的子进程,等system()自己调用waitpid()等待子进程时,目标子进程已经被提前回收 - 显式将
SIGCHLD的处理动作设置为SIG_IGN(忽略信号),该设置下内核会在子进程退出后直接回收其资源,不会保留僵尸进程供父进程wait,父进程调用waitpid会直接返回ECHILD - 进程内其他独立线程中存在循环调用wait回收所有子进程的逻辑,抢在
system()之前回收了它创建的子进程
多线程环境下信号处理是进程全局生效的,只要任意一个线程修改了信号处理配置,所有线程的system()调用都会受影响。另外提供的复现代码存在一处笔误:全局错误码变量名为errno,代码中写的errorno是拼写错误,会导致打印的错误值不准。
修复方案
- 如果需要自定义
SIGCHLD处理逻辑回收业务代码自行创建的子进程,不要在信号处理函数中使用waitpid(-1, ...)无差别回收所有子进程,只回收自己记录过PID的业务子进程即可。 - 调用
system()前后临时保存并恢复信号处理配置,避免自定义信号逻辑干扰system()的内部执行,可直接复用如下安全封装:
#include <signal.h> #include <errno.h> #include <stdlib.h> int safe_system(const char* cmd) { struct sigaction sa_chld_old, sa_quit_old, sa_ign; sigset_t block_mask, old_mask; int ret, saved_errno; // 临时屏蔽SIGCHLD、SIGQUIT避免竞态 sigemptyset(&block_mask); sigaddset(&block_mask, SIGCHLD); sigaddset(&block_mask, SIGQUIT); sigprocmask(SIG_BLOCK, &block_mask, &old_mask); // 按POSIX规范,system执行期间需要忽略SIGQUIT,SIGCHLD设为默认处理 sa_ign.sa_handler = SIG_IGN; sigemptyset(&sa_ign.sa_mask); sa_ign.sa_flags = 0; sigaction(SIGCHLD, &sa_ign, &sa_chld_old); sigaction(SIGQUIT, &sa_ign, &sa_quit_old); ret = system(cmd); saved_errno = errno; // 提前保存errno,避免后续函数调用覆盖该值 // 恢复原有信号配置 sigaction(SIGCHLD, &sa_chld_old, NULL); sigaction(SIGQUIT, &sa_quit_old, NULL); sigprocmask(SIG_SETMASK, &old_mask, NULL); errno = saved_errno; return ret; }
- 不要在后台常驻线程中无差别循环wait所有子进程,子进程回收逻辑需要全局统一管理,明确区分业务自行创建的子进程和库函数内部创建的子进程。
- 修正代码中的拼写错误,将
errorno改为标准的errno,且在系统调用返回后第一时间保存errno值,避免其他库函数(如printf)覆盖errno导致错误信息打印不准。
内容的提问来源于stack exchange,提问作者sanvi
相关产品推荐
相关产品推荐

