Linux,分叉进程立即挂起

时间:2015-04-28 12:19:58

标签: c linux fork

我遇到了叉子的问题,只是偶尔发生。它基本上一直有效,但在测试系统中偶尔会失败。

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

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

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

如果我通过fork启动子进程:

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

显示问题的伪代码:

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调用中内存不足,会发生什么?这是否会引发信号(由于被阻止而无法传递)?

1 个答案:

答案 0 :(得分:3)

我从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;
}

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

<强>解释

syslog就是问题!

一般情况下: 非异步信号安全功能 是问题所在!

如Damian Pietras所述(见链接页面)

  

在子节点中调用任何非异步安全的函数(man 7 signal)   fork()调用多线程程序后的进程是未定义的   行为

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

这个答案归功于Adrien Descamps找到根本原因(系统日志),还有PSkocik和Jan Spurny检测源(LOG_MSG)。