Linux,分叉进程立即挂起

我遇到了叉子的问题,只发生零星。 它基本上一直工作,但在testing系统上每隔一段时间就会失败。

我的研究没有提到任何人提到类似的问题。

该问题发生在embedded式Linux系统上。 没有交换分区可用。

正在运行的进程在所有线程中都阻塞了所有信号,并通过专用线程中的sigtimedwait处理它们。

如果我通过fork启动一个subprocess:

  • 父进程继续返回值> 0.所以分支工作。 没有 -1返回 – 所以没有错误,不是内存不足! 父母然后等待subprocess,永远不会从等待中返回。
  • subprocess永远不会做任何可观察的事情。 孩subprocess应该做的第一件事就是写一条日志消息。 此日志消息从不出现。 那么它应该产生两个subprocess超时进程和一个工作进程。 这些过程从不出现。
  • 如果我通过命令行上的ps进行检查,我可以看到现有的subprocess。 它处于状态S (可中断睡眠(等待事件完成))。 它从来没有得到任何CPU时间,它没有显示CPU使用率。
  • 如果我杀了-9subprocess,父进程完成等待,并继续愉快。

显示问题的伪码:

const pid_t childPid = fork(); if(0 == childPid) { // child process LOG_MSG("Child process started."); // <- This never shows up in the syslog. // do some stuff } else if(-1 == childPid) { // error LOG_MSG("Parent process: Error starting child process!"); result = false; } else { // parent process LOG_MSG("Parent process: Child process started. PID: %.", childPid); // <- This shows up in the syslog. // do some stuff int status = 0; const int options = 0; const auto waitResult = waitpid(childPid, &status, options); // more stuff } 

问题:

  1. 什么可能导致这个悬挂儿童的过程?
  2. 如果新进程在导致syslog的LOG_MSG调用中内存不足,会发生什么情况? 这会产生一个信号(因为阻塞而无法传送)?

我从Adrien Descamps的链接 (也见上面的注释)和C ++ – ified中稍微修改了一下:

 #include <thread> #include <iostream> #include <atomic> #include <unistd.h> #include <syslog.h> #include <sys/wait.h> std::atomic<bool> go(true); void syslogBlaster() { int j = 0; while(go) { for(int i = 0; i < 100; ++i) { syslog(LOG_INFO, "syslogBlaster: %d@%d", i, j); } ++j; std::this_thread::sleep_for(std::chrono::milliseconds(30)); } } int main() { std::thread blaster(syslogBlaster); for(int i = 0; i < 1000; ++i) { const auto forkResult = fork(); if(0 == forkResult) { syslog(LOG_INFO, "Child process: '%d'.", static_cast<int>(getpid())); exit(0); } else if(forkResult < 0) { std::cout << "fork() failed!" << std::endl; } else { syslog(LOG_INFO, "Parent process."); std::cout << "Waiting #" << i << "!" << std::endl; int status = 0; const int options = 0; const auto waitResult = waitpid(forkResult, &status, options); if(-1 == waitResult) { std::cout << "waitpid() failed!"; } else { std::cout << "Bye zombie #" << i << "!" << std::endl; } } std::this_thread::sleep_for(std::chrono::milliseconds(28)); } go = false; blaster.join(); std::cout << "Wow, we survived!" << std::endl; } 

运行此示例,该过程在第一次和第五次尝试之间卡住(在我的设备上)。

说明

系统日志是问题!

一般来说: 非异步信号安全功能是问题!

正如Damian Pietras所说(参见链接页面)

在多线程程序中调用fork()之后调用子进程中不是异步安全(man 7信号)的任何函数具有未定义的行为

从技术上来说,问题(未定义的行为)是由于关键部分中的数据不一致(因为叉中间的分支不是正确的线程),或者像这种情况一样 – 来自被锁定的互斥在父母身上,然后永远留在孩子身上。

这个答案的信用转到Adrien Descamps寻找根本原因(系统日志),但也为PSkocik和Jan Spurny检测源(LOG_MSG)。