1. 程式人生 > >Linux:可重入函式與不可重入函式

Linux:可重入函式與不可重入函式

可重入函式

         一個函式在執行的過程中被打斷,然後會再被從頭執行一次,執行完後,再回來把剛才沒執行完的部分執行完。這就相當於巢狀的執行了。函式是公共程式碼,這樣的執行是允許的。函式的執行可以被打斷,打斷之後還可以再從頭執行,執行完後接著執行剛才沒有執行的程式碼,然後第一次執行的程式碼(被打斷的函式)執行結果還是正確的。也就是說,這個函式執行,無論中間把這個函式再嵌入執行多少遍,怎麼嵌入,最終執行完都不會對函式內部功能/程式邏輯造成影響,並且執行的結果都是正確的,這樣的函式就是可重入函式。

       我們平常所寫的函式,常常都是用的函式內部的區域性變數,由於是函式內部的區域性變數,那麼如果出現了函式在執行的過程中被打斷,然後被從頭執行,執行完畢後,由於我們使用的都是函式內部的區域性變數,會釋放在函式內部所建立的區域性變數等,這樣等我們返回到之前被中斷的地方去的時候,就不會影響此時這個函式的內部的變數內容,也就是說我們的函式是可重入函式。

常用的可重入函式:

  1. 不要使用全域性變數,防止別的程式碼覆蓋這些變數的值。 
  2. 呼叫這類函式之前先關掉中斷,呼叫完之後馬上開啟中斷。防止函式執行期間被中斷進入別的任務執行。 
  3. 使用訊號量(互斥條件)。 

總而言之:要保證中斷是安全的。

        我們平常寫的一些在上建立的函式,如果此函式被重入,多次重入時,互相之間並不會影響它們的指標指向。所以在重入時是可以的。

不可重入函式

        函式執行期間,被中斷,從頭執行這個函式,執行完畢後再返回剛才的中斷點繼續執行,此時由於剛才的中斷導致了現在從新在中斷點執行時發生了不可預料的錯誤。也就是說,如果函式在不同的地方/時序進行呼叫,會對函式的功能邏輯造成影響,這種函式就稱為不可重入函式。

        我們知道,在中斷函式之後會將中斷點的一些資料儲存起來,如上下文資料等,那麼這些資料不是被儲存了嗎?怎麼還會被影響?其實我們在儲存上下文資料的時候,僅僅儲存了一小部分,而且那一小部分也只是地址,而對全域性變數、靜態變數這些並沒有儲存。所以一旦中斷之後,返回到中斷點,此時就是不可預料的了。

常見的不可重入函式:

  1. 使用了靜態資料結構
  2. 呼叫了malloc和free等
  3. 呼叫了標準I/O函式
  4. 進行了浮點運算

      malloc與free是不可重入的,它們使用了全域性變數來指向區。標準I/O大多都使用了全域性資料結構。

      浮點一般都是不可重入的 (浮點運算大多使用協處理器或者軟體模擬來實現)。  

  • 一個函式中判斷是否是可重入函式: 

               1.是否對全域性性(static)的資料進行修改操作

               2.這個操作是否是原子性(不可被打斷)的

       不可重入函式:對全域性性變數的進行修改操作,且這個操作不是原子性的

  • 如何避免寫出不可重入函式

        不使用或者互斥使用全域性變數,不使用靜態區域性變數,只使用區域性變數;在函式中動態分配的記憶體只在本函式中使用,不會傳遞函式外使用;只要保證區域性特性,函式中使用的所有東西都只有區域性性,對外不公開,用完即釋放,就可以保證可重入。這樣,不管怎麼重疊,反正本層的函式的東西只有本層能夠使用,其他層的函式無法使用,就相當於隔離每一層的變數。這樣,不管重疊多少次,都不可能相互影響。這樣每一層函式執行的結果都是正確的。