C語言函數重入
- 可重入函數:可以被中斷的函數,即這個函數執行時,可以中斷其執行,可以由一個或多個任務並發使用,而不比擔心數據錯誤。
- 不可重入函數(不安全函數)
- 不能運行在多任務環境下,除非能保證互斥(使用信號量/代碼的關鍵部分禁用中斷)
- 是由於使用了未受保護的系統資源,如全局變量區,中斷向量表等。
可重入函數:
- 沒有靜態數據結構
- 不返回指向靜態數據的指針
- 所有函數數據由函數的調用者提供
- 使用auto變量,或通過全局變量的拷貝來保護全局變量
- 若必須訪問全局變量,則利用互斥信號保護
- 不調用不可重入函數
不可重入函數有:
如果一個函數在重入條件下使用了未受保護的共享的資源,那么它是不可重入的。
- 函數中使用了靜態變量,無論是全局靜態變量還是局部靜態變量。
- 函數返回靜態變量
- 函數中調用了不可重入函數
- 函數體內使用了靜態的數據結構
- 函數體內調用了malloc()或者free()函數
- 函數體內調用了其他標准I/O函數
- 函數是singleton中的成員函數,而且使用了不使用線程獨立存儲的成員變量
不可重入函數改寫成可重入函數:
1、不使用全局變量
2、在和硬件發生交互時,可能會發生中斷時,先關閉中斷(有些系列叫做“進入/退出核心”或者用OS_ENTER_KERNAL/OS_EXIT_KERNAL來描述,這是臨界區保護)
3、不調用不可重入函數
4、謹慎使用堆棧,最好在使用前OS_ENTER_KERNAL
中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴展—讓標准C支持中斷。中斷服務子程序ISR:
__interrupt double compute_area (double radius) { double area = PI * radius * radius; printf("\nArea = %f", area); return area; }
錯誤:
1、ISR沒有返回值
如果它有返回值,返回給誰,某種中斷源產生中斷,系統使用ISR進行處理。ISR是鏈接在某一個中斷源上的,而中斷源的產生是隨機的,所以ISR並沒有一個固定的調用者,也沒有固定的返回地址,所以返回值沒有用。
2、ISR不能傳遞參數
3、在許多編譯器/處理器中,浮點數是不可重入的。有些不允許在ISR中做浮點運算。
4、ISR應該短、有效率的,做浮點數不明智。
5、printf是不可重入函數。
解釋:
1、浮點數:一般浮點運算都是由專門的硬件來完成,舉個例子假設有個硬件寄存器名字叫做FLOAT,用來計算和存放浮點數的中間運算結果。
假設有這么個函數
void fun() { //...這個函數對FLOAT寄存器進行操作 }
假如第一次執行,有個對浮點數操作運算的結果臨時存在FLOAT寄存器中,而就在這時被中斷了,而中斷函數或者另一個進程也調用fun函數,這時第二次調用的fun函數在執行的過程中就會破壞第一次FLOAT寄存器中的結果,這樣當返回到第一次fun函數的時候,結果就不正確了。
2、printf函數
引用全局變量stdout,這是標准輸入輸出流的一個對象
malloc --------全局內存分配表
free --------全局內存分配表
在unix里面通常都有加上_r后綴的同名可重入函數版本。