這個概念在嵌入式操作系統中比較重要,由於存在任務的調度,它實時系統,可剝奪型內核中是危險的,如同一個安靜的水雷。可能會被觸發,也可能安然無恙。由於它運行結果的不可預期性,會使系統帶來隱患。
printf()經常有重入解釋
不可重入函數不可以在它還沒有返回就再次被調用。例如printf,malloc,free等都是不可重入函數。因爲中斷可能在任何時候發生,例如在printf執行過程中,因此不能在中斷處理函數裏調用printf,否則printf將會被重入。
函數不可重入大多數是因爲在函數中引用了全局變量。例如,printf會引用全局變量stdout,malloc,free會引用全局的內存分配表。
個人理解:如果中斷髮生的時候,當運行到printf的時候,假設發生了中斷嵌套,而此時stdout資源被佔用,所以第二個中斷printf等待第一個中斷的stdout資源釋放,第一個中斷等待第二個中斷返回,造成了死鎖,不知這樣理解對不對。
|
|
不可重入函數指的是該函數在被調用還沒有結束以前,再次被調用可能會產生錯誤。可重入函數不存在這樣的問題。
不可重入函數在實現時候通常使用了全局的資源,在多線程的環境下,如果沒有很好的處理數據保護和互斥訪問,就會發生錯誤。
常見的不可重入函數有:
printf --------引用全局變量stdout
malloc --------全局內存分配表
free --------全局內存分配表
在unix裏面通常都有加上_r後綴的同名可重入函數版本。如果實在沒有,不妨在可預見的發生錯誤的地方嘗試加上保護鎖同步機制等等。 |
下面引用一段別人的解釋:
這主要在多任務環境中使用,一個可重入的函數簡單來說,就是:可以被中斷的函數。就是說,你可以在這個函數執行的任何時候中斷他的運行,在OS的調度下去執行另外一段代碼而不會出現什麼錯誤。而不可重入的函數由於使用了一些系統資源,比如全局變量區,中斷向量表等等,所以他如果被中斷的話,可能出現問題,所以這類函數是不能運行在多任務環境下的。
把一個不可重入函數變成可重入的唯一方法是用可重入規則來重寫他。
其實很簡單,只要遵守了幾條很容易理解的規則,那麼寫出來的函數就是可重入的。
第一,不要使用全局變量。因爲別的代碼很可能覆蓋這些變量值。
第二,在和硬件發生交互的時候,切記執行類似disinterrupt()之類的操作,就是關閉硬件中斷。完成交互記得打開中斷,在有些系列上,這叫做“進入/退出核心”或者用OS_ENTER_KERNAL/OS_EXIT_KERNAL來描述。
第三,不能調用任何不可重入的函數。
第四,謹慎使用堆棧。最好先在使用前先OS_ENTER_KERNAL。
還有一些規則,都是很好理解的,總之,時刻記住一句話:保證中斷是安全的!
通俗的來講吧:由於中斷是可能隨時發生的,斷點位置也是無法預期的。所以必須保證每個函數都具有不被中斷髮生,壓棧,轉向ISR,彈棧後繼續執行影響的穩定性。也就是說具有不會被中斷影響的能力。既然有這個要求,你提供和編寫的每個函數就不能拿公共的資源或者是變量來使用,因爲該函數使用的同時,ISR(中斷服務程序)也可那會去修改或者是獲取這個資源,從而有可能使中斷返回之後,這部分公用的資源已經面目全非。
滿足下列條件的函數多數是不可重入的:
(1)函數體內使用了靜態的數據結構;
(2)函數體內調用了malloc()或者free()函數;
(3)函數體內調用了標準I/O函數。
下面舉例加以說明。
可重入函數
void strcpy(char* lpszDest, char* lpszSrc)
{
while(*lpszDest++ = *lpszSrc++);
*dest=0;
}
非可重入函數1
char cTemp; // 全局變量
void SwapChar1(char* lpcX, char* lpcY)
{
cTemp = *lpcX;
*lpcX = *lpcY;
lpcY = cTemp; // 訪問了全局變量,在分享內存的多個線程中可能造成問題
}
非可重入函數2
void SwapChar2(char* lpcX, char* lpcY)
{
static char cTemp; // 靜態局部變量
cTemp = *lpcX;
*lpcX = *lpcY;
lpcY = cTemp; // 使用了靜態局部變量,在分享內存的多個線程中可能造成問題
}
如何寫出可重入的函數?在函數體內不訪問那些全局變量,不使用靜態局部變量,堅持只使用局部變量,寫出的函數就將是可重入的。如果必須訪問全局變量,記住利用互斥信號量來保護全局變量。