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 容器对您的应用程序的响应(包括崩溃),如下所示:
- 使用
tini
作为容器的入口点。
- 将
tini
运行 的脚本设为 CMD
- 您的脚本将使用您的 cronjob 更新 crontab 文件(由您定义 运行 的频率)
- 添加的 cron 将 运行 您的实际 运行 脚本。
- 您的 运行 脚本应该有一个
trap
函数。为什么?一旦您的应用程序崩溃,您可以向您的入口点脚本(第 2 点)发送一个 KILL 信号,这将终止 docker 容器。通过这种方式,您可以保持 运行将您的应用程序作为入口点的行为。
希望这对您的案例有所帮助。
我有以下 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 容器对您的应用程序的响应(包括崩溃),如下所示:
- 使用
tini
作为容器的入口点。 - 将
tini
运行 的脚本设为CMD
- 您的脚本将使用您的 cronjob 更新 crontab 文件(由您定义 运行 的频率)
- 添加的 cron 将 运行 您的实际 运行 脚本。
- 您的 运行 脚本应该有一个
trap
函数。为什么?一旦您的应用程序崩溃,您可以向您的入口点脚本(第 2 点)发送一个 KILL 信号,这将终止 docker 容器。通过这种方式,您可以保持 运行将您的应用程序作为入口点的行为。
希望这对您的案例有所帮助。