可重入函數與線程安全
一組並發線程運行在同一進程上下文中,每一個線程都有自己獨立的線程上下文,包括線程ID、棧、棧指針、程序計數器、條件碼和通用目的寄存器。每個線程和其他線程一起共享進程上下文的其他部分,包括整個用戶虛擬地址空間(由代碼段、讀/寫數據、堆以及所有共享庫的代碼和數據區組成)。線程也共享打開的文件集合。當存在共享資源的時候,對資源的訪問需要同步。這時候使用線程編寫程序的時候,需要編寫具有線程安全性屬性的函數。一個函數,當且僅當被多個並發線程反復調用時,能夠一直產生正確的結果,才能夠被稱為線程安全的,否則我們稱其為非線程安全的。
可重入的特點:
由於可重入函數多次調用不會出錯,所以不必擔心數據被破壞;
可重入函數在任何時候都可以被中斷,一段時間後又可以運行,相應的數據不會丟失;
可重入函數只使用局部變量,即保存在CPU寄存器或者堆棧中,使用全局變量要加以保護;
不可重入的特點:
使用malloc/free函數,malloc函數是用全局鏈表來管理堆棧的;
調用標準I/O庫函數,標準I/O庫的很多實現都以不可重入的方式使用全局數據結構;
可重入體內使用了靜態數據結構;
常見的不可重入函數有:
printf----引用了全局變量stdout
malloc---全局內存分配表
free------全局內存分配表
線程安全與可重入:
可重入的定義源自於單線程環境。在單線程環境中,一段代碼在執行中可能會被硬件中斷,並轉而調用中斷服務程序(ISR)。在這次調用中斷處理函數之前,有可能中斷處理函數已經在執行。因此,任何中斷處理函數都應該是可重入的。
線程安全的概念則是源自於多線程環境。起源不一樣,那麽他們之間也沒有什麽必然的關系。
面試題:
中斷是嵌入式系統中重要的組成部分。新的關鍵字_interrupt。下面的代碼就是用_interrupt關鍵字去定義了一個中斷服務子程序(ISR),然後評論下面的代碼;
__interrupt double compute_area (double radius) { double area = PI * radius * radius; printf("\nArea = %f", area); return area; }
代碼錯誤:
1、ISR不能返回一個值。
2、ISR不能傳遞參數。
3、在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額外的寄存器入棧,有些處理器/編譯器你就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。
4、printf()經常有重入和性能上的問題。
那麽在這裏就該有一個原則:
1、不要使用static變量和全局變量,堅持只用局部變量
2、如必須使用全局變量,利用互斥信號量來保護全局變量
3、獲取得知哪些系統調用是可重入的,在多任務處理程序中都使用安全的系統調用
4、不調用其他任何不可重入的函數
5、謹慎使用malloc/free
可重入函數與線程安全