说一个进程内的线程A正在调用printf,另一个线程 接收到信号,然后调用printf。是否可能是因为这里的内核 不知道该怎么做,因为它不能够区分这两个调用。
这不是内核会有问题。这是你的应用程序本身。 printf
不是内核函数。它是C库中的一个函数,即您的应用程序使用。 printf
实际上是一个相当复杂的功能。它支持各种输出格式。
此格式化的最终结果是写入标准输出的格式化输出字符串。这个过程本身也涉及一些工作。格式化的输出字符串被写入内部stdout
文件句柄的输出缓冲区。只要某些定义的条件发生,即输出缓冲区已满,和/或每当新行字符被写入时,输出缓冲区就会被刷新(只有在此时内核接管并将定义的数据块写入文件)输出流。
所有这些都由输出缓冲区的内部数据结构支持,您不必担心它,因为它是C库的工作。现在,信号可以在任何时候到达,而printf
它可以工作。我的意思是,在任何时候。在更新输出缓冲区内部数据结构的过程中,printf
可能会很好到达,并且它们处于暂时不一致的状态,因为printf
尚未完成更新。
示例:在现代C/C++实现中,printf
可能不是信号安全的,但它是线程安全的。多个线程可以使用printf
来写入标准输出。线程的责任是协调这个过程,确保最终的输出真正有意义,并且不会随机地从多个线程的输出中混淆,但这并不重要。
问题是printf
是线程安全的,这通常意味着某个地方有一个mutex参与该过程。因此,可能发生的事件序列为:
现在,内部mutex
被锁定。关于信号处理程序的一点是,通常不会指定进程中的哪个线程处理信号。给定的实现可能会随机选择一个线程,或者它可能总是选择当前正在运行的线程。在任何情况下,它都可以选择锁定printf
的线程,以处理信号。
所以,现在,您的信号处理程序运行,并且它还决定致电printf
。由于printf
的内部互斥锁被锁定,所以线程必须等待互斥锁被解锁。
然后等待。
然后等待。
因为,如果您正在跟踪事情:互斥锁被被中断以处理信号的线程锁定。线程继续运行之前,互斥锁不会解锁。但是直到信号处理程序终止,线程才会恢复运行,但是信号处理程序现在正在等待互斥体被解锁。
你被绑架了。
现在,当然,printf
可能会使用C++等效的std::recursive_mutex
来避免这个问题,但即使这样也不能解决可能由信号引入的所有可能的死锁。
总而言之,为什么它“不安全地接收信号并从该信号处理程序中调用非异步安全功能”是因为根据定义它不是。从信号处理程序中调用非异步安全函数是不安全的,因为信号是一个异步事件,并且由于它不是一个异步安全函数,所以根据定义,不能这样做。水是湿的,因为它是水,并且不能从异步信号处理程序调用异步不安全函数
简写形式是“因为标准是这么说的”,或者“因为你的图书馆是这样说的”,长形式是代码不是用异步访问能力:有很多方法可以编写代码,以便它不能很好地处理异步问题。几乎所有处理异步的方法在同步运行时都会产生成本,因此具有非异步处理功能会使性能常常受到影响 – Yakk
感谢!如果我以这样的方式构造我的代码,以便不以并行方式调用非异步安全函数,但是在非返回信号处理程序的上下文中,那没关系吗? – Curious
'malloc'特定版本:https://stackoverflow.com/questions/3366307/why-is-malloc-not-async-signal-safe –