說一個進程內的線程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 –