docker 容器中 C++ 应用程序的信号处理程序不工作

Signal handlers of C++ application within a docker container are not working

我有以下 C++ 代码:

#include <signal>
#include <iostream>

void sig_handler(int signo, siginfo_t *info, void *_ctx) {
      
    cout << "HANDLE AND RESET!!" << endl;

    // try to forward the signal.
    raise(info->si_signo);

    // terminate the process immediately.
    puts("watf? exit");
    _exit(EXIT_FAILURE);
}


void registerSignalHandlers() {
    vector<int> signals = {
      // Signals for which the default action is "Core".
      SIGABRT, // Abort signal from abort(3)
      SIGBUS,  // Bus error (bad memory access)
      SIGFPE,  // Floating point exception
      SIGILL,  // Illegal Instruction
      SIGIOT,  // IOT trap. A synonym for SIGABRT
      SIGQUIT, // Quit from keyboard
      SIGSEGV, // Invalid memory reference
      SIGSYS,  // Bad argument to routine (SVr4)
      SIGTRAP, // Trace/breakpoint trap
      SIGXCPU, // CPU time limit exceeded (4.2BSD)
      SIGXFSZ, // File size limit exceeded (4.2BSD)
      SIGTERM
    };
    
    
    for (size_t i = 0; i < signals.size(); ++i) {
      struct sigaction action;
      memset(&action, 0, sizeof action);
      action.sa_flags = static_cast<int>(SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND);
      sigfillset(&action.sa_mask);
      sigdelset(&action.sa_mask, signals[i]);
      action.sa_sigaction = &sig_handler;

      int r = sigaction(signals[i], &action, nullptr);
    }

}



int main(int argc, char *argv[]) {
    
    registerSignalHandlers();

    //rest of the code goes here

    return 0;
}

我 运行 我的应用程序在 docker 容器中,带有 debian:stretch 图像,tini 作为 PID 1(直接使用它而不是通过 bash) .当我 运行 我的设备 (MacBook Pro) 上的容器时,一切正常,没有任何问题。我尝试在我的代码中引起分段错误 SIGSEGV 异常以测试处理程序触发器,在我的设备上,它工作得很好但是一旦我 运行 服务器上的完全相同的容器(CentOS 7)处理程序是根本不工作。

我所说的根本不工作是指我的应用程序从未收到信号。我尝试从容器 kill -15 PID_OF_APPLICATION 内部手动发送信号,并且处理程序正常工作,但如果发送 kill -11 PID_OF_APPLICATION 处理程序不起作用,不知道为什么!

我尝试使用 strace 检查我的代码是否引发了信号,我能够看到引发了 SIGSEGV

此外,我尝试 运行 一个脚本,该脚本 运行 我的应用程序和 trap 它接收到的信号。在脚本中收到了信号,但也没有触发处理程序

我不确定我是否遗漏了与 docker 容器配置相关的内容(我使用的是 docker-compose),但我认为自从相同的 docker-我设备上的撰写文件正在启动容器,并且没有任何问题。

容器内的应用程序发出的信号是否也通过 PID 1 处理? tini 就我而言。

非常感谢任何帮助

更新

如果我将入口点设置为 sleep infinity 并使用 docker exec -it container_id bash 进入 docker 容器,然后手动启动我的应用程序作为前台进程,处理程序可以正常工作

我以前去过那里。这是一场真正的斗争,尤其是当事情在您的设备上正常运行但在其他设备上却不正常时。

我会写下我为自己的问题所做的步骤,也许它可以为您提供一些解决方案。

一切在 macOS 上运行完美,我不得不比较我的设备和服务器之间的 docker 引擎版本,它是 CentOS7,但没有区别!

然后我尝试在 Ubuntu 上使用 docker-compose 而不是 CentOS7 运行 相同的 docker 图像,这就是惊喜!它也能正常工作,所以我的问题只出现在 CentOS7 上。我建议你也这样做,尝试 运行 你的 docker 图像在另一个 OS 上,比如 Ubuntu 只是为了确保你的问题与你的实际应用无关。

尝试 运行 使用 cronjob

你的应用程序

是的,尝试 运行 它作为一个 cronjob。我不确定问题的根本原因,但这对我有用。我认为这与当您 运行 容器 Here 时 docker 代理如何发出信号有关,您会在 README 文件的末尾找到有关信号的有用结论,具体取决于您 运行 你的容器。

此外,另一个可能的原因是当应用程序 运行 在后台运行时,信号不会以某种方式代理到它,因此 运行 将其作为 cronjob 运行与常规运行不同后台进程。

您可以将此方法作为一个完整的解决方案来管理,并维护 docker 容器对您的应用程序的响应(包括崩溃),如下所示:

  1. 使用 tini 作为容器的入口点。
  2. tini 运行 的脚本设为 CMD
  3. 您的脚本将使用您的 cronjob 更新 crontab 文件(由您定义 运行 的频率)
  4. 添加的 cron 将 运行 您的实际 运行 脚本。
  5. 您的 运行 脚本应该有一个 trap 函数。为什么?一旦您的应用程序崩溃,您可以向您的入口点脚本(第 2 点)发送一个 KILL 信号,这将终止 docker 容器。通过这种方式,您可以保持 运行将您的应用程序作为入口点的行为。

希望这对您的案例有所帮助。