異步信號安全(可重入性)與線程安全

書中10.6和12.5兩節分別是信號和線程的重入介紹。但是未對異步信號安全、線程安全、可重入概念做統一對比,難以徹悟。針對於此,寫下本文。

1. 三個概念,線程安全,可重入,信號安全

  先簡單提一下,

  線程安全,主要是針對數據競爭來說的,就是說:如果數據不需要共享,那就讓每個線程私有;如果需要共享,那就加鎖。

  信號安全,其實也就是異步信號安全,是說線程在信號處理函數當中,不管以任何方式調用你的這個函數如果不死鎖不修改數據,那就是信號安全的。也就是說一個可重入函數在信號處理函數當中不影響調用他的人本身的狀態,其實就是一個task_struct有很多指針指向它,你通過多線程調一個可重入函數,函數有自己所在的task的代碼段和數據段,如果你在信號處理裏面調它,實際上是換了一個指向同一個task_struct裏面內容的指針來操作,會有問題。這種情形通過加鎖是解決不了問題的,比如你正在調用printf過程中,遇到一個信號,轉而去處理這個信號,並且在處理這個信號的信號處理函數當中正巧要調用printf,那麼屏幕上就是亂的,加鎖行不行呢?那就很好玩了。所以printf不是異步信號安全的。

  可重入性:就是無論以什麼方式多次調用都不會出現問題,不會出現對可能有修改的靜態數據的訪問,不會出現對全局變量(比如errno)的訪問。嚴格講可重入要區分線程安全(弱可重入)還是信號安全(強可重入)兩點,但是一般說可重入就是指信號安全。由於信號安全要求高於線程安全,所以說如果一個函數是可重入的,那一定是線程安全的(反之不一定)。

2.  下面詳細解釋三者

  2.1 可重入性(reentrant針對函數,它有兩個方面的內涵:

  1)可並行/併發,同時進入:指可重入函數被某任務調用時,其它任務可同時進行調用而不產生錯誤的結果;或稱在相同的輸入情況下可重入函數的執行所產生的效果,並不因其併發的調用而產生不同,也稱併發安全

  2)中斷後可重新進入:指可重入函數可被任意的中斷,當中斷執行完畢,返回斷點可以繼續正確的運行下去;或稱在相同的輸入情況下可重入函數的執行所產生的結果,並不因爲在函數執行期間有中斷的調用而產生不同,也稱中斷安全

  可重入(reentrant)函數可以由多於一個任務併發使用,而不必擔心數據錯誤。相反, 不可重入(non-reentrant)函數不能由超過一個任務所共享,除非能確保函數的互斥 (或者使用信號量,或者在代碼的關鍵部分禁用中斷)。可重入函數可以在任意時刻被中斷, 稍後再繼續運行,不會丟失數據。可重入函數要麼使用本地變量,要麼在使用全局變量時 保護自己的數據。

  可重入函數一般要滿足:

  • 不爲連續的調用持有靜態數據。
  • 不返回指向靜態數據的指針;所有數據都由函數的調用者提供。
  • 使用本地數據,或者通過製作全局數據的本地拷貝來保護全局數據。
  • 絕不調用任何不可重入函數。 

  出於以下任意某個原因,其餘函數是不可重入的:

  • 它們調用了 malloc 或 free:malloc/free 是在 C 標準中規定的,而在 C 中是沒有進程/線程的概念。
  • 衆所周知它們使用了靜態數據結構體。
  • 它們是標準 I/O 程序庫的一部分:glibc的c庫算是線程安全了,卻不保證可重入或異步信號安全。

  2.2 線程安全(MT-safe)不僅僅針對函數,它主要是指數據或程序的安全這一結果,所以有不同的層次上講的線程安全:如線程安全的函數,線程安全的庫 。本文還會引入線程安全的類這一概念。通常意義上一個線程安全的函數是指有可重入函數第一個內涵的函數即併發安全的函數。但需要注意的是即使是一個從函數級別上並不安全的函數,如果使其不安全的因素在特定應用中並不存在時,這個函數對於該應用來講同樣也是線程安全的。例如對於全局變量的訪問,一般而言未命名同步方式訪問肯定是非線程安全的,但如果所有可能同時發生的訪問均是隻讀訪問則從結果上講也是線程安全的。

  不要混淆可重入與線程安全。在程序員看來,這是兩個獨立的概念:函數可以是可重入的,是線程安全的,或者 二者皆是,或者二者皆非。不可重入的函數不能由多個線程使用。另外,或許不可能讓某個 不可重入的函數是線程安全的。關於標準IO庫是不是線程安全的要依據實際情況而定,不同的庫有不同實現,一般爲了方便用戶使用,在支持多線程的系統上都配備了線程安全的庫,但卻不是以不安全的,因爲,信號一定是步的,不是線程的加鎖機制(本質是同步化)就可以解決的

  2.3 信號安全,信號的本質軟中斷,中斷在本質上是異步的,所謂異步信號安全同線程安全一樣,也是站在結果上考慮的,指在信號的中斷處理過程中的安全。通常意義上一個異步信號安全的函數是指可以在異步信息處理函數中調用而不會出現異常的函數。同樣需要注意到即使一個從函數級別上並非異步信息安全的函數,如果在信息處理函數中調用,也並不一定會產生不安全的結果。

3. 三個概念的異同

   從函數級別考慮,僅從概念上就可以發現可重入函數一定是線程安全函數,也是異步信號安全函數;多線程安全函數卻要弱得多,並非一定要是可重入函數,它只要求併發無誤即可;雖然異步信號函數與可重入函數的描述方式有所不同,但兩者從實現層面上講是完全一致的。

 

參考文獻:

《使用可重入函數進行更安全的信號處理》

http://bbs.chinaunix.net/thread-942090-1-1.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章