Sunday, January 9, 2022

[Linux Kernel] 重入

printf() 為什麼有重入和效能上的問題

不可重入函式不可以在它還沒有返回就再次被呼叫。例如printf,malloc,free等都是不可重入函式。

因為中斷可能在任何時候發生,例如在printf執行過程中,因此不能在中斷處理函式裡呼叫printf,否則printf將會被重入。

函式不可重入大多數是因為在函式中引用了全域性變數。例如,printf會引用全域性變數stdout,malloc,free會引用全域性的記憶體分配表

常見的不可重入函式有:

printf --------引用全域性變數stdout

malloc --------全域性記憶體分配表

free    --------全域性記憶體分配表


其實很簡單,只要遵守了幾條很容易理解的規則,那麼寫出來的函式就是可重入的。

第一,不要使用全域性變數。因為別的程式碼很可能覆蓋這些變數值。

第二,在和硬體發生互動的時候,切記執行類似disinterrupt()之類的操作,就是關閉硬體中斷。完成互動記得開啟中斷,在有些系列上,這叫做“進入/退出核心”或者用OS_ENTER_KERNAL/OS_EXIT_KERNAL來描述。

第三,不能呼叫任何不可重入的函式。

第四,謹慎使用堆疊。最好先在使用前先OS_ENTER_KERNAL。

還有一些規則,都是很好理解的,總之,時刻記住一句話:保證中斷是安全的!


通俗的來講吧:由於中斷是可能隨時發生的,斷點位置也是無法預期的。所以必須保證每個函式都具有不被中斷發生,壓棧,轉向ISR,彈棧後繼續執行影響的穩定性。也就是說具有不會被中斷影響的能力。既然有這個要求,你提供和編寫的每個函式就不能拿公共的資源或者是變數來使用,因為該函式使用的同時,ISR(中斷服務程式)也可那會去修改或者是獲取這個資源,從而有可能使中斷返回之後,這部分公用的資源已經面目全非。


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

(1)函式體內使用了靜態的資料結構;

(2)函式體內呼叫了malloc()或者free()函式;

(3)函式體內呼叫了標準I/O函式。


非可重入函式1

char cTemp; // 全域性變數

void SwapChar1(char* lpcX, char* lpcY)

{

  cTemp = *lpcX; 

  *lpcX = *lpcY; 

  lpcY = cTemp; // 訪問了全域性變數,在分享記憶體的多個執行緒中可能造成問題

}

非可重入函式2

void SwapChar2(char* lpcX, char* lpcY)

{

  static char cTemp; // 靜態區域性變數

  cTemp = *lpcX; 

  *lpcX = *lpcY; 

  lpcY = cTemp; // 使用了靜態區域性變數,在分享記憶體的多個執行緒中可能造成問題

}

性能上的問題:

上述的問題也屬於性能問題。對於併發或同時執行的多進程或任務,若要正確使用printf()函數,必須互斥使用,在一個進程或任務執行printf ()時,其他使用printf()的進程或任務只能等待,不能及時顯示。

No comments:

Post a Comment

n8n index

 【n8n免費本地端部署】Windows版|程式安裝x指令大補帖  【一鍵安裝 n8n】圖文教學,獲得無限額度自動化工具&限時免費升級企業版功能