我遇到了叉子的问题,只发生零星。 它基本上一直工作,但在testing系统上每隔一段时间就会失败。
我的研究没有提到任何人提到类似的问题。
该问题发生在embedded式Linux系统上。 没有交换分区可用。
正在运行的进程在所有线程中都阻塞了所有信号,并通过专用线程中的sigtimedwait处理它们。
如果我通过fork启动一个subprocess:
显示问题的伪码:
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 }
问题:
我从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)。