信号处理程序中的 C++ 打印?

问题描述

搜索了很多,但没有人回答我的问题,我读到在这样的信号处理程序中使用 cout 是不安全的:

void ctrlZHandler(int sig_num) {
    //SIGTSTP-18
    std::cout << "smash: got ctrl-Z" << std::endl;
    SmallShell::route_signal(sig_num);
}
  1. 如果我将打印移动到 route_signal 中,它会解决问题吗?

  2. 是否有 C++11 中安全调用函数的列表?

  3. 如果使用 write 的唯一解决方案是什么,你能给我展示一个简短的例子,假设 route_signal 有 100 个打印,我应该用 write() 替换所有打印吗?这听起来很累,需要分配内存和释放......

解决方法

信号处理程序需要快速运行并且是可重入的,这就是为什么它们不应该直接或间接调用输出流函数,如 cout

如果您在受控条件下临时执行此操作以进行测试,则可能没问题,但请确保在处理程序完成之前不会再次触发您正在处理的信号,并注意流函数可能很慢,这可能会造成混乱还要进行测试。

,

不推荐在信号处理程序中使用 std::cout 的原因是因为每当 std::cout::operator << 不可重入时,信号可能会中断您正在运行的代码。

这意味着如果您在发出信号时执行 std::cout::operator << 并且在执行过程中也使用它,结果是未定义的。

所以,不。将其移入 route_signal 并不能解决此问题,您应该替换其中的 std::cout 的每个调用!

一种解决方法是设置一个标志,表明该信号已被接收,并在信号返回后在信号处理程序之外创建一个输出。

,
  1. 如果我将打印移动到 route_signal 中,它会解决问题吗?

没有

  1. C++11 中是否有安全调用函数的列表?

出于实际目的,您可以做的唯一安全的事情是在信号处理程序中设置 volatile sig_atomic_t 或无锁原子标志。 (N3690 intro.execution §1.9 ¶6)

我不是 C 或 C++ 语言律师,但我相信在 C++11 信号处理程序中允许符合 C 应用程序中允许的任何内容。但是,该集合非常非常有限:abortquick_exit_Exitsignal。 (ISO/IEC 9899:2011 §7.14.1.1 ¶5)。

  1. 如果使用 write 的唯一解决方案是什么,你能给我展示一个简短的例子,假设 route_signal 有 100 个打印,我应该用 write() 替换所有打印吗?这听起来很累,需要分配内存和释放......

更好的解决方案是重新设计您的程序以使用 sigwait 或检查在信号处理程序中安全设置的标志。

如果你坚持使用 write,并且如果你相信在你的 C++ 实现中调用信号处理程序是安全的——这可能是安全的,但同样,C++ 本身并不能保证——那么你只是有一个编码问题。您需要自己弄清楚格式化,请记住,即使在符合 POSIX 的系统上,mallocfree 也不是异步信号安全的。当然可以。