【问题标题】:Can I execute free() or close() in a signal handler? [duplicate]我可以在信号处理程序中执行 free() 或 close() 吗? [复制]
【发布时间】:2021-03-22 01:15:39
【问题描述】:

我的代码如下所示:

//global variables

void signal_handler() {
    //deallocation of global variables
    free(foo);
    close(foo_2);
    exit(0);
}

int main () {
    signal(SIGINT, signal_handler);

    //irrelevant code
}

如您所见,我将CTRL+C 中断更改为执行一次signal_handler 函数,而不是立即终止进程。我在某处读到,某些函数(例如 free)不是异步安全的,并且不会在 signal_handler 中执行,但我不确定。

我可以在信号处理程序中执行freecloseexit 甚至pthread_join 之类的函数吗?

【问题讨论】:

  • 没有。 asdfasdfasfsadfas
  • 你在编写什么样的软件?一些网络事物、一些科学代码(例如CAD 软件)或一些驱动核导弹的 C 代码?当您的软件崩溃时,您会发生什么?
  • 这和How to avoid using printf() in a signal handler?基本是同一个问题。有一个可以在信号处理程序中调用的函数列表(close() 是一个,但 fclose() 不是;free()exit() 都不是,但您可以安全地调用 _exit()_Exit() ; pthread_join() 不能从信号处理程序中调用)。

标签: c signals


【解决方案1】:

没有。只有man 7 signal-safety 中列出的函数可以安全地在信号处理程序中调用。

close 已列出并且应该是安全的。 free 不是。出于您必须查看其源代码的原因(它包含锁)。 exit 不安全,因为它可以调用任意清理处理程序。你有_exit,它在没有清理的情况下突然退出。

【讨论】:

  • 注意:pthread_join 在其当前的 glibc 实现中对我来说看起来很安全,但该标准不保证其他实现或未来版本也会如此。手册页确实提到了其他一些 pthread_* 函数,因此缺少 pthread_join 应被解释为不安全。
【解决方案2】:

从技术上讲,您可以编译一个在信号处理程序中调用此类函数的程序,没有什么能阻止您这样做。但是,如果您尝试执行的函数不是异步信号安全的,它将导致未定义的行为。不像你说的那样,不安全的函数只是“不执行”,它们很可能,但这仍然是未定义的行为。

man 7 signal-safety 中记录了异步信号安全函数的列表。 close() 函数是安全的,而 free()phtread_join() 则不是。从信号处理程序调用exit() 函数也是安全的,如果您希望退出此类上下文,则必须改用_exit()

在接收信号时安全调用非异步信号安全的函数的唯一方法是“记住”你必须调用它(例如设置一个全局变量),然后在 从信号处理程序返回。

【讨论】:

  • 如果你有什么导致 UB 的东西,你甚至在达到那个点之前就得到了 UB。所以从技术上讲你不能按照 C 标准,因为导致 UB 的程序根本什么都不做。
  • free 可能会死锁,如果线程在内存分配例程中被中断。
  • @SvenNilsson 哦,是的,当然可以,这是调用未定义行为时的无限可能性之一:')
  • @MarcoBonelli 实际上是的。但不是按照C标准。如果您的程序将来有时会到达 UB,那么使用程序已经拥有的信息,即使在到达那个点之前也没有任何保证。您的编译器可能知道 free() 不能在信号处理程序中调用并优化掉 signal(SIGINT, signal_handler); 调用。
  • @12431234123412341234123 我会告诉你“编译器优化假设没有未定义的行为”等等。虽然我认为这根本不是一回事,但它必须按照 C99 记录3.4.3.2 美元。无论如何,是的,我将重新措辞以在两种情况下都清楚。
【解决方案3】:

简短的回答是否定的:

7.1.4 库函数的使用
...
4     标准库中的函数不保证是可重入的,可能会修改 具有静态或线程存储持续时间的对象。188)
188) 因此,信号处理程序通常不能调用标准库函数
C 2011 Online Draft

结果的真实例子 - 我在一个与 Access 数据库通信的系统上工作。有一个信号处理程序试图使用fprintf 向控制台写入错误消息,但在信号处理过程中,stderr 不知何故被映射到存储数据库的 .mdb 文件,覆盖了标题并破坏了数据库修复。

说实话,除了设置一个标志以在其他地方检查之外,您可以在信号处理程序中做很多事情。

【讨论】:

  • 请注意,标准 C 只允许每个实现都可以支持的操作——非常少的操作集。 POSIX 对系统提出了更严格的要求,并且有大量函数可以从信号处理程序中安全使用——正如我对重复问题的回答中所指出的那样。
  • C11 §7.14.1.1 The signal function ¶5 指定规则: 如果信号不是由于调用abortraise 函数而出现的,如果信号处理程序引用任何具有静态或线程存储持续时间但不是无锁原子对象的对象,除非将值分配给声明为volatile sig_atomic_t 的对象,或者信号处理程序调用标准库中除abort 函数之外的任何函数,_Exit 函数,quick_exit 函数,…_[…继续…]
  • [...continuation...] ... signal 函数,其第一个参数等于导致调用处理程序的信号对应的信号编号.此外,如果对 signal 函数的此类调用导致 SIG_ERR 返回,则 errno 的值是不确定的。 它还引用了脚注 252,其中指出:如果任何信号由异步信号处理程序生成, 行为是未定义的。 POSIX 提供了更合理的语义,并且其他平台同样扩展了已定义的行为,超出了 C 标准允许的范围(这是非常少的)。
【解决方案4】:

我可以在信号处理程序中执行 free() 或 close() 吗?

你绝对不应该。见signal(7)signal-safety(7)

在实践中,它可能在一半以上的时间都按照您的意愿运行。 IIRC,GCC 编译器正在按照您的意愿运行,并且通常可以正常运行。

更好的方法是使用一些write(2)pipe(7)(从您的信号处理程序内部),并不时使用poll(2) 或相关的东西检查该管道(在您的主程序中)。

或者你可以在你的信号处理程序中设置一些volatile sigatomic_t flag;(也许它也应该是_Atomic),并在其他地方检查该标志(在主程序中,在信号处理程序之外)。

Qt is explaining that better 比我在几分钟内完成的。

在 Linux 上,另请参阅 signalfd(2)eventfd(2)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    • 2013-05-27
    相关资源
    最近更新 更多