c++ - 为什么只能从信号处理程序安全地调用异步信号安全函数?

标签 c++ linux multithreading c++11 signals

我仍然有点困惑,为什么从该信号处理程序中接收信号并调用非异步安全函数是不安全的。有人可以解释这背后的原因,并可能尝试给我一些引用资料,我可以按照这些引用资料自己阅读更多内容吗?

换句话说,我在问为什么在信号处理程序中调用 printf 是不安全的。是因为进程内问题和可能的竞争条件是由于两次可能的 printf 调用而导致的可能没有保护,还是因为进程间竞争相同的资源(在本例中为标准输出)。假设进程 A 中的一个线程正在调用 printf,另一个线程接收到信号然后调用 printf。是否可能是因为这里的内核将不知道要做什么,因为它将无法区分这两个调用。

最佳答案

Say a thread within process A is calling printf and another thread receives the signal and then calls printf. Is it possibly because the kernel here will not know what to do because it will not be able to distinguish between the two calls.


不是内核会出现问题。这是您的应用程序本身。 printf不是核函数。它是您的应用程序使用的 C 库中的一个函数。 printf实际上是一个相当复杂的函数。它支持多种输出格式。
此格式化的最终结果是写入标准输出的格式化输出字符串。这个过程本身也涉及一些工作。格式化的输出字符串被写入内部 stdout文件句柄的输出缓冲区。每当某些定义的条件发生时,即当输出缓冲区已满和/或每当一个换行符被写入时,输出缓冲区就会被刷新(并且仅在此时内核接管并将定义的数据块写入文件)输出流。
所有这些都由输出缓冲区的内部数据结构支持,您不必担心,因为这是 C 库的工作。现在,信号可以到达任何时间点 printf做它的工作。我的意思是,在任何时候。它很可能到达同时 printf正在更新输出缓冲区的内部数据结构,并且它们处于暂时不一致的状态,因为 printf还没更新完呢。
示例:在现代 C/C++ 实现中,printf可能不是信号安全的,但它是线程安全的。多线程可以使用printf写入标准输出。线程有责任在它们之间协调这个过程,以确保最终输出实际上是有意义的,并且它不会从多个线程的输出中随机困惑,但这不是重点。
重点是printf是线程安全的,这通常意味着某处有 mutex参与的过程。因此,可能发生的事件序列是:
  • printf获取内部互斥锁。
  • printf继续格式化字符串并将其写入 stdout的输出缓冲区。
  • 之前 printf完成,可以释放获取到的互斥锁,一个信号到达。

  • 现在,内部mutex锁住了。关于信号处理程序的事情是通常没有指定进程中的哪个线程来处理信号。一个给定的实现可能会随机选择一个线程,或者它可能总是选择当前正在运行的线程。无论如何,它肯定可以挑出锁定printf的线程。 ,这里是为了处理信号。
    所以现在,你的信号处理程序运行,它也决定调用 printf .因为 printf的内部互斥锁被锁定,线程必须等待互斥锁解锁。
    等等。
    等等。
    因为,如果您正在跟踪事物:互斥锁被中断以服务信号的线程锁定。在线程恢复运行之前,互斥锁不会被解锁。但这不会发生,直到信号处理程序终止,线程恢复运行,但信号处理程序现在正在等待互斥锁被解锁。
    你有骨气。
    现在,当然,printf可能会使用 C++ 等效的 std::recursive_mutex , 以避免这个问题,但即使这样也不能解决所有可能由信号引入的死锁。
    总而言之,“接收信号并从该信号处理程序中调用非异步安全函数是不安全的”的原因是,根据定义,它不是。从信号处理程序中调用非异步安全函数是不安全的”,因为信号是一个异步事件,而且由于它不是一个异步安全函数,根据定义,你不能。水是湿的,因为它是水,并且不能从异步信号处理程序调用异步不安全函数。

    关于c++ - 为什么只能从信号处理程序安全地调用异步信号安全函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34467694/

    相关文章:

    c++ - 使用Doxygen记录枚举类值而不启用EXTRACT_ALL

    如果没有文件,linux shell脚本从stdin读取

    python - cProfile 配置文件在线程内调用吗?

    c - fork 和现有线程?

    c++ - 为什么在将临时变量传递给线程函数时移动构造函数会被调用两次?

    java - C++ 与 Java : endless loop creating objects only crashes C++

    c++ - 使用精确大小的整数c++

    c++ - 关于如何将旧式嵌入式应用程序限制为实时应用程序的建议

    linux - 如何使用 XDebug 分析 Codeigniter Web 应用程序?

    linux - 移动文件的脚本在 Perl 中不起作用