可重入函數和不可重入函數的概念
在函數中如果我們使用靜態變量了,導致產生中斷調用別的函數的 過程中可能還會調用這個函數,於是原來的 靜態變量被在這里改變了,然后返回主體函數,用着的那個靜態變量就被改變了,導致錯誤。這類函數我們稱為不可重入函數。
如果是在函數體內 動態申請內存的話,即便 新的線程調用這個函數也沒事,因為新的線程使用的是新的函數的 新申請的動態內存(靜態變量只有一份,所以 多線程對於函數體內的靜態變量改變 會有無法修復的結果),所以這類函數就是可重入函數。
在 實時系統的設計中,經常會出現多個任務調用同一個函數的情況。如果這個函數不幸被設計成為不可重入的函數的話,那么不同任務調用這個函數時
可能修改其他任 務調用這個函數的數據,從而導致不可預料的后果。那么什么是可重入函數呢?所謂可重入是指一個可以被多個任務調用的過程,任務在
調用時不必擔心數據是否會 出錯。不可重入函數在實時系統設計中被視為不安全函數。
滿足下列條件的函數多數是不可重入的:
(1)函數體內使用了靜態的數據結構;
(2)函數體內調用了malloc()或者free()函數;
(3)函數體內調用了標准I/O函數。
如何寫出可重入的函數?在函數體內不訪問那些全局變量,不使用靜態局部變量,堅持只使用缺省態(auto)局部變量,寫出的函數就將是可重入的。如果
必須訪問全局變量,記住利用互斥信號量來保護全局變量。或者調用該函數前關中斷,調用后再開中斷。
可重入函數可以被一個以上的任務調用,而不必擔心數據被破壞。可重入函數任何時候都可以被中斷,一段時間以后又可以運行,而相應的數據不會丟失。
可重入函數或者只使用局部變量,即保存在CPU寄存器中或堆棧中;或者使用全局變量,則要對全局變量予以保護。
說法2:
一個可重入的函數簡單來說,就是:可以被中斷的函數。就是說,你可以在這個函數執行的任何時候中斷他的運行,在任務調度下去執行另外一段代 碼而
不會出現什么錯誤。而不可重入的函數由於使用了一些系統資源,比如全局變量區,中斷向量表等等,所以他如果被中斷的話,可能出現問題,所以這類函
數是 不能運行在多任務環境下的。
基本上下面的函數是不可重入的
(1)函數體內使用了靜態的數據結構;
(2)函數體內調用了malloc()或者free()函數;
(3)函數體內調用了標准I/O函數。
把一個不可重入函數變成可重入的唯一方法是用可重入規則來重寫他。
其實很簡單,只要遵守了幾條很容易理解的規則,那么寫出來的函數就是可重入的。
第一,不要使用全局變量。因為別的代碼很可能覆蓋這些變量值。
第二,在和硬件發生交互的時候,切記執行類似disinterrupt()之類的操作,就是關閉硬件中斷。完成交互記得打開中斷,在有些系列上,這叫做“進入/退出核
心”或者用OS_ENTER_KERNAL/OS_EXIT_KERNAL來描述。//這是臨界區保護
第三,不能調用任何不可重入的函數。
第四,謹慎使用堆棧。最好先在使用前先OS_ENTER_KERNAL。
還有一些規則,都是很好理解的,總之,時刻記住一句話:保證中斷是安全的!
中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴展—讓標准C支持中斷。具代表事實是,產生了一個新的關鍵字 __interrupt。下
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf("\nArea = %f", area);
return area;
}
下面引用一段別人的解釋:
這主要在多任務環境中使用,一個可重入的函數簡單來說,就是:可以被中斷的函數。就是說,你可以在這個函數執行的任何時候中斷他的運行,在OS的
把一個不可重入函數變成可重入的唯一方法是用可重入規則來重寫他。
其實很簡單,只要遵守了幾條很容易理解的規則,那么寫出來的函數就是可重入的。
第一,不要使用全局變量。因為別的代碼很可能覆蓋這些變量值。
第二,在和硬件發生交互的時候,切記執行類似disinterrupt()之類的操作,就是關閉硬件中斷。完成交互記得打開中斷,在有些系列上,這叫做“進入/退出核
第三,不能調用任何不可重入的函數。
第四,謹慎使用堆棧。最好先在使用前先OS_ENTER_KERNAL。
還有一些規則,都是很好理解的,總之,時刻記住一句話:保證中斷是安全的!
通俗的來講吧:由於中斷是可能隨時發生的,斷點位置也是無法預期的。所以必須保證每個函數都具有不被中斷發生,壓棧,轉向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; // 使用了靜態局部變量,在分享內存的多個線程中可能造成問題
}
如何寫出可重入的函數?在函數體內不訪問那些全局變量,不使用靜態局部變量,堅持只使用局部變量,寫出的函數就將是可重入的。如果必須訪問全局
變量,記住利用互斥信號量來保護全局變量。