RT-Thread 之臨界段保護
1、什麼是臨界段
臨界段就是一段在執行的時候不能被中斷的程式碼段。在RT-Thread裡面這個臨界段最常出現的就是對全域性變數的操作。
那麼什麼情況下臨界段會被打斷? 一個是系統排程,另一個是外部中斷。在RT-Thread裡面,系統排程,最終也是產生PendSV中斷,在PendSV Handler裡面實現執行緒的切換。所以還是可以歸結為中斷。既然這樣,RT-Thread對臨界段的保護就處理得很乾脆了,直接把中斷全部關了,NMIFAULT和硬FAULT除外。
2、cortex-M 核心快速關中斷指令
為了快速開關中斷,cortex-M 核心專門設定了一條CPS指令,有4種用法,具體如下:
CPSID I ;PRIMASK=1PRIMASK 和 FAULTMASK 是 Cortex-M 核心裡面三個中斷遮蔽暫存器中的兩個,還有一個是 BASEPRI,有關這三個暫存器的詳細用法見下表。; 關中斷 CPSIE I ;PRIMASK=0 ; 開中斷 CPSID F ;FAULTMASK=1; 關異常 CPSIE F ;FAULTMASK=0; 開異常
3、關中斷
RT-Thread 關中斷的函式在 contex_rvds.s 中定義,在 rthw.h 中宣告,具體實現:;/* ; * rt_base_t rt_hw_interrupt_disable(); ; */ rt_hw_interrupt_disable PROC; (1) EXPORT rt_hw_interrupt_disable; (2) MRS r0, PRIMASK; (3) CPSID I; (4) BX LR; (5) ENDP; (6)
(1)關鍵字PROC表示彙編子程式的開始
(2)使用EXPORT 關鍵字匯出標號 rt_hw_interrupt_disable,使其具有全域性屬性,在外部標頭檔案聲明後(在rthw.h中宣告),就可以在C檔案中呼叫。
(3)通過MRS指令將特殊暫存器PRIMASK的值儲存到通用暫存器r0中,當在C中調用匯編子程式返回時,會將r0作為函式返回值。所以在C中呼叫rt_hw_interrupt_disable()時,需要事先宣告一個變數用來儲存rt_hw_interrupt_disable()的返回值,即r0暫存器的值,也就是PRIMASK的值。
(4)關閉中斷,即使用CPS指令將PRIMASK暫存器的值置1,
在這裡,我敢肯定,一定會有人有這樣一個疑問:關中斷,不就是直接使用 CPSID I 指令就行了嘛,為什麼還要第三步,即在執行 CPSIDI 指令前,要先把 PRIMASK 的值儲存起來?這個疑問接下來在“臨界段程式碼的應用”這個小結揭曉。(5)子程式返回
(6)ENDP表示彙編子程式結束,與PROC成對使用。
4、開中斷
RT-Thread 開中斷的函式在 contex_rvds.s 中定義,在 rthw.h 中宣告,具體實現見:;/* ; * void rt_hw_interrupt_enable(rt_base_t level); ; */ rt_hw_interrupt_enable PROC; (1) EXPORT rt_hw_interrupt_enable; (2) MSR PRIMASK, r0 (3) BX LR; (4) ENDP; (5)
(1)關鍵字PROC表示彙編子程式開始。
(2)使用EXPORT匯出標號rt_hw_interrupt_disable(),使其具有全域性屬性,在外部標頭檔案中聲明後(在rthw.h中宣告),就可以在C檔案中呼叫。
(3)通過MRS指令將通用暫存器r0的值儲存到特殊暫存器PRIMASK中,當在C中調用匯編子程式返回時,會將第一個形參傳入到通用暫存器r0。所以在C中呼叫rt_hw_interrupt_disable()的時候,需要傳入一個形參。該形參是進入臨界段之前儲存的PRIMASK的值。這個時候又有人會問,開中斷,不就是使用 CPSIE I 指令就行了
嘛,為啥跟我等凡人想的不一樣?其中奧妙將在接下來“臨界段程式碼的應用”這個小結揭曉。 (4)子程式返回 (5)ENDP彙編子程式結束,與PROC成對使用。