Linux,分叉进程立即挂起

Linux, forked process hangs immediately

我遇到了一个偶尔出现的 fork 问题。它基本上一直有效,但在测试系统上偶尔会失败。

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

问题发生在嵌入式 Linux 系统上。没有可用的交换分区。

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

如果我通过 fork 启动一个 child 进程:

显示问题的伪代码:

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. 什么会导致这个挂起的 child 进程?
  2. 如果新进程在导致 syslog 的 LOG_MSG 调用中内存不足,会发生什么情况?这会发出信号吗(由于被阻塞而无法传送)?

我从 Adrien Descamps' link 中获取了示例(另请参阅上面的评论)和 C++ 化并对其进行了一些修改:

#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 所述(参见链接页面)

calling any function that is not async-safe (man 7 signal) in child process after fork() call in a multi-threaded program has undefined behaviour

从技术上讲,问题(未定义的行为)是由关键部分中的数据不一致引起的(因为 不是 的线程恰好在它的中间fork) 或 - 就像在这种情况下 - 来自锁定在父级中的互斥锁,然后在子级中永远保持这种方式。

此答案归功于 Adrien Descamps 寻找根本原因 (syslog),也归功于 PSkocik 和 Jan Spurny 检测来源 (LOG_MSG)。