主要用於多任務環境中,一個可重入的函數簡單來講就是能夠被中斷的函數,也就是說,能夠在這個函數執行的任什麼時候刻中斷它,轉入OS調度下去執行另一段代碼,而返回控制時不會出現什麼錯誤;而不可重入的函數因爲使用了一些系統資源,好比全局變量區,中斷向量表等,因此它若是被中斷的話,可能會出現問題,這類函數是不能運行在多任務環境下的。 也能夠這樣理解,重入即表示重複進入,首先它意味着這個函數能夠被中斷,其次意味着它除了使用本身棧上的變量之外不依賴於任何環境(包括static),這樣的函數就是purecode(純代碼)可重入,能夠容許有該函數的多個副本在運行,因爲它們使用的是分離的棧,因此不會互相干擾。若是確實須要訪問全局變量(包括static),必定要注意實施互斥手段。可重入函數在並行運行環境中很是重要,可是通常要爲訪問全局變量付出一些性能代價。 編寫可重入函數時,若使用全局變量,則應經過關中斷、信號量(即P、V操做)等手段對其加以保護。 說明:若對所使用的全局變量不加以保護,則此函數就不具備可重入性,即當多個進程調用此函數時,頗有可能使有關全局變量變爲不可知狀態。 示例:假設Exam是int型全局變量,函數Squre_Exam返回Exam平方值。那麼以下函數不具備可重入性。 unsigned int example( int para ) { unsigned int temp; Exam = para; // (**) temp = Square_Exam( ); return temp; } 此函數若被多個進程調用的話,其結果多是未知的,由於當(**)語句剛執行完後,另一個使用本函數的進程可能正好被激活,那麼當新激活的進程執行到此函數時,將使Exam賦與另外一個不一樣的para值,因此當控制從新回到「temp = Square_Exam( )」後,計算出的temp極可能不是預想中的結果。此函數應以下改進。 unsigned int example( int para ) { unsigned int temp; [申請信號量操做] //(1) Exam = para; temp = Square_Exam( ); [釋放信號量操做] return temp; } (1)若申請不到「信號量」,說明另外的進程正處於給Exam賦值並計算其平方過程當中(即正在使用此信號),本進程必須等待其釋放信號後,纔可繼續執行。若申請到信號,則可繼續執行,但其它進程必須等待本進程釋放信號量後,才能再使用本信號。 保證函數的可重入性的方法: 在寫函數時候儘可能使用局部變量(例如寄存器、堆棧中的變量),對於要使用的全局變量要加以保護(如採起關中斷、信號量等方法),這樣構成的函數就必定是一個可重入的函數。 VxWorks中採起的可重入的技術有: * 動態堆棧變量(各子函數有本身獨立的堆棧空間) * 受保護的全局變量和靜態變量 * 任務變量 -------------------------------------------------- 在實時系統的設計中,常常會出現多個任務調用同一個函數的狀況。若是這個函數不幸被設計成爲不可重入的函數的話,那麼不一樣任務調用這個函數時可能修改其餘任務調用這個函數的數據,從而致使不可預料的後果。那麼什麼是可重入函數呢?所謂可重入函數是指一個能夠被多個任務調用的過程,任務在調用時沒必要擔憂數據是否會出錯。不可重入函數在實時系統設計中被視爲不安全函數。知足下列條件的函數多數是不可重入的: 1) 函數體內使用了靜態的數據結構; 2) 函數體內調用了malloc()或者free()函數; 3) 函數體內調用了標準I/O函數。 下面舉例加以說明。 A. 可重入函數 void strcpy(char *lpszDest, char *lpszSrc) { while(*lpszDest++=*lpszSrc++); *dest=0; } B. 不可重入函數1 charcTemp;//全局變量 void SwapChar1(char *lpcX, char *lpcY) { cTemp=*lpcX; *lpcX=*lpcY; lpcY=cTemp;//訪問了全局變量 } C. 不可重入函數2 void SwapChar2(char *lpcX,char *lpcY) { static char cTemp;//靜態局部變量 cTemp=*lpcX; *lpcX=*lpcY; lpcY=cTemp;//使用了靜態局部變量 } 問題1,如何編寫可重入的函數? 答:在函數體內不訪問那些全局變量,不使用靜態局部變量,堅持只使用局部變量,寫出的函數就將是可重入的。若是必須訪問全局變量,記住利用互斥信號量來保護全局變量。 問題2,如何將一個不可重入的函數改寫成可重入的函數? 答:把一個不可重入函數變成可重入的惟一方法是用可重入規則來重寫它。其實很簡單,只要遵照了幾條很容易理解的規則,那麼寫出來的函數就是可重入的。 1) 不要使用全局變量。由於別的代碼極可能覆蓋這些變量值。 2) 在和硬件發生交互的時候,切記執行相似disinterrupt()之類的操做,就是關閉硬件中斷。完成交互記得打開中斷,在有些系列上,這叫作「進入/退出核心」。 3) 不能調用其它任何不可重入的函數。 4) 謹慎使用堆棧。最好先在使用前先OS_ENTER_KERNAL。 堆棧操做涉及內存分配,稍不留神就會形成益出致使覆蓋其餘任務的數據,因此,請謹慎使用堆棧!最好別用!不少***程序就利用了這一點以便系統執行非法代碼從而輕鬆得到系統控制權。還有一些規則,總之,時刻記住一句話:保證中斷是安全的! 實例問題:曾經設計過以下一個函數,在代碼檢視的時候被提醒有bug,由於這個函數是不可重入的,爲何? unsigned int sum_int( unsigned int base ) { unsigned int index; static unsigned int sum = 0; // 注意,是static類型 for (index = 1; index <= base; index++) sum += index; return sum; } 分析:所謂的函數是可重入的(也能夠說是可預測的),即只要輸入數據相同就應產生相同的輸出。這個函數之因此是不可預測的,就是由於函數中使用了static變量,由於static變量的特徵,這樣的函數被稱爲:帶「內部存儲器」功能的的函數。所以若是須要一個可重入的函數,必定要避免函數中使用static變量,這種函數中的static變量,使用原則是,能不用盡可能不用。 將上面的函數修改成可重入的函數,只要將聲明sum變量中的static關鍵字去掉,變量sum即變爲一個auto類型的變量,函數即變爲一個可重入的函數。 固然,有些時候,在函數中是必需要使用static變量的,好比當某函數的返回值爲指針類型時,則必須是static的局部變量的地址做爲返回值,若爲auto類型,則返回爲錯指針。