## 可重入函數
### 可重入性的理解
若一個程序或子程序可以安全的被并行執行,則稱其為可重入的;即當該子程序正在運行時,可以再次進入并執行它。若一個函數是可重入的,則該函數必須滿足一下必要條件:
1. 不能含有靜態(全局)非常量數據。
2. 不能返回靜態(全局)非常量數據的地址。
3. 只能處理由調用者提供的數據。作為可重入函數的輸入參數,只能由調用者提供,而且所提供的輸入數據必須滿足上面兩點要求
4. 不能依賴于單實例模式資源的鎖。
5. 不能調用不可重入的函數。
6. 函數內部,盡量不能用 malloc 和 free 之類的方法進行內存分配和釋放,如果使用,一般情況下會造成該函數的不可重入.
可重入函數主要用于多任務環境中。可重入的函數必須滿足以下三個條件:
(1)可以在執行的過程中可以被打斷;
(2)被打斷之后,在該函數一次調用執行完之前,可以再次被調用(或進入,reentered)。
(3)再次調用執行完之后,被打斷的上次調用可以繼續恢復執行,并正確執行。
### 不可重入函數的示例
如果一個函數只有調用者提供的非靜態變量參數,并且函數中只依賴自己棧上的環境,也沒有調用其他的不可重入函數,那么他就一定是可重入的.這樣的函數就是purecode(純代碼)可重入,可以允許有該函數的多個副本在運行,由于它們使用的是分離的棧,所以不會互相干擾。如果確實需要訪問全局變量(包括 static),一定要注意實施互斥手段。可重入函數在并行運行環境中非常重要,但是一般要為訪問全局變量付出一些性能代價。
編寫可重入函數時,若使用全局變量,則應通過關中斷、信號量(即P、V操作)等手段對其加以保護。
說明:若對所使用的全局變量不加以保護,則此函數就不具有可重入性,即當多個進程調用此函數時,很有可能使有關全局變量變為不可知狀態。
```cpp
int Exam = 0;
unsigned int example( int para )
{
unsigned int temp;
Exam = para; // (**)
temp = Square_Exam( );
return temp;
}
```
此函數若被多個進程調用的話,其結果可能是未知的,因為當(**)語句剛執行完后,另外一個使用本函數的進程可能正好被激活,那么當新激活的進程執行到此函數時,將使 Exam 賦與另一個不同的 para 值,所以當控制重新回到 “temp = Square_Exam( )” 后,計算出的temp很可能不是預想中的結果。此函數應如下改進。
```cpp
view plaincopy在CODE上查看代碼片派生到我的代碼片
int Exam = 0;
unsigned int example( int para )
{
unsigned int temp;
[申請信號量操作] //(1)? 加鎖
Exam = para;
temp = Square_Exam( );
[釋放信號量操作] //? ? 解鎖
return temp;
}
```
申請不到“信號量”,說明另外的進程正處于給 Exam賦值并計算其平方過程中(即正在使用此信號),本進程必須等待其釋放信號后,才可繼續執行。若申請到信號,則可繼續執行,但其它進程必須等待本進程釋放信號量后,才能再使用本信號。
保證函數的可重入性的方法:
1. 在寫函數時候盡量使用局部變量(例如寄存器、堆棧中的變量);
2. 對于要使用的全局變量要加以保護(如采取關中斷、信號量等互斥方法),這樣構成的函數就一定是一個可重入的函數。
滿足下列條件的函數多數是不可重入(不安全)的:
1. 函數體內使用了靜態的數據結構;
2. 函數體內調用了malloc() 或者 free() 函數;
3. 函數體內調用了標準 I/O 函數。