C語言可重入函式和不可重入函式

2022-06-30 22:03:11 字數 3775 閱讀 4973

在函式中如果我們使用靜態變數了,導致產生中斷呼叫別的函式的 過程中可能還會呼叫這個函式,於是原來的 靜態變數被在這裡改變了,然後返回主體函式,用著的那個靜態變數就被改變了,導致錯誤。這類函式我們稱為不可重入函式

如果是在函式體內 動態申請記憶體的話,即便 新的執行緒呼叫這個函式也沒事,因為新的執行緒使用的是新的函式的 新申請的動態記憶體(靜態變數只有乙份,所以 多執行緒對於函式體內的靜態變數改變 會有無法修復的結果),所以這類函式就是可重入函式

在 實時系統的設計中,經常會出現多個任務呼叫同乙個函式的情況。如果這個函式不幸被設計成為不可重入的函式的話,那麼不同任務呼叫這個函式時

可能修改其他任 務呼叫這個函式的資料,從而導致不可預料的後果。那麼什麼是可重入函式呢?所謂可重入是指乙個可以被多個任務呼叫的過程,任務在

呼叫時不必擔心資料是否會 出錯。不可重入函式在實時系統設計中被視為不安全函式。

滿足下列條件的函式多數是不可重入的:

(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) 

這個函式有太多的錯誤了,以至讓人不知從何說起了:

1)isr 不能返回乙個值。如果你不懂這個,那麼你不會被雇用的。

2) isr 不能傳遞引數。如果你沒有看到這一點,你被雇用的機會等同第一項。

3) 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額處的暫存器入棧,有些處理器/編譯器就是不允許在isr中做浮點運

算。此外,isr應該是短而有效率的,在isr中做浮點運算是不明智的。

4) 與第三點一脈相承,printf()經常有重入和效能上的問題。如果你丟掉了第三和第四點,我不會太為難你的。不用說,如果你能得到後兩點,那麼你的被

雇用前景越來越光明了。

-->

如果說中斷服務程式有返回值,那麼它的值返回給誰呢?

在系統的執行過程中,一定是某種中斷源出發了相應的中斷,系統上掛接的中斷服務程式進行現場的處理,例如告警等操作,然後清中斷。

也就是說中斷服務程式鏈結在某一類中斷源上,而這些中斷源的產生是隨機的,所以,中斷服務程式並沒有乙個固定的呼叫者,也沒有固定的返回位址,所

以返回值也沒有用

我的問題是,這裡所說的printf()經常有重入的問題,具體是指什麼?有人能給解釋一下麼這個概念在嵌入式作業系統中比較重要,由於存在任務的排程,它

實時系統,可剝奪型核心中是危險的,如同乙個安靜的水雷。可能會被觸發,也可能安然無恙。由於它執行結果的不可預期性,會使系統帶來隱患。

下面引用一段別人的解釋:

這主要在多工環境中使用,乙個可重入的函式簡單來說,就是:可以被中斷的函式。就是說,你可以在這個函式執行的任何時候中斷他的執行,在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)

非可重入函式1:

char ctemp;            // 全域性變數

void swapchar1(char* lpcx, char* lpcy)

非可重入函式2:

void swapchar2(char* lpcx, char* lpcy)

如何寫出可重入的函式?在函式體內不訪問那些全域性變數,不使用靜態區域性變數,堅持只使用區域性變數,寫出的函式就將是可重入的。如果必須訪問全域性

變數,記住利用互斥訊號量來保護全域性變數。  

c語言可重入函式和不可重入函式

可重入函式 函式可多執行緒同時呼叫,一般是在只有區域性變數的函式中 不可重入函式 函式一般情況下不可多執行緒同時呼叫,這樣的函式中一般有全域性 靜態變數,而這樣就涉及到了變數的保護問題,所以需要加入互斥鎖 訊號量 中斷。我們現在舉例來說明吧!可重入函式 void swap temp int p in...

C 可重入函式和不可重入函式

可重入函式是指能夠被多個執行緒 同時 呼叫的函式,並且能保證函式結果正確不必擔心資料錯誤的函式。不可重入函式是指不能執行在多工環境下,除非能保證互斥的函式。可重入函式特點 不可重入函式特點 可重入規則改寫不可重入函式 把乙個不可重入函式變成可重入的唯一方法是用可重入規則來重寫它。可重入規則 不要使用...

可重入函式和不可重入函式

可重入函式 可重入函式主要用於多工環境中,乙個可重入函式簡單來說是可以被中斷的函式,即可以在函式執行的任何時刻中斷它,轉入os排程下去執行另一段 而返回控制時不出現錯誤,這意味著它除了使用自己棧上的變數以外,不依賴於任何環境 包括static 這樣的函式就是purecode 純 可重入,它可以允許該...