1. 程式人生 > 其它 >RT-Thread 之臨界段保護

RT-Thread 之臨界段保護

1、什麼是臨界段

臨界段就是一段在執行的時候不能被中斷的程式碼段。在RT-Thread裡面這個臨界段最常出現的就是對全域性變數的操作。

  那麼什麼情況下臨界段會被打斷? 一個是系統排程,另一個是外部中斷。在RT-Thread裡面,系統排程,最終也是產生PendSV中斷,在PendSV Handler裡面實現執行緒的切換。所以還是可以歸結為中斷。既然這樣,RT-Thread對臨界段的保護就處理得很乾脆了,直接把中斷全部關了,NMIFAULT和硬FAULT除外。

2、cortex-M 核心快速關中斷指令

為了快速開關中斷,cortex-M 核心專門設定了一條CPS指令,有4種用法,具體如下:

CPSID I ;PRIMASK=1
; 關中斷 CPSIE I ;PRIMASK=0 ; 開中斷 CPSID F ;FAULTMASK=1; 關異常 CPSIE F ;FAULTMASK=0; 開異常
PRIMASK 和 FAULTMASK 是 Cortex-M 核心裡面三個中斷遮蔽暫存器中的兩個,還有一個是 BASEPRI,有關這三個暫存器的詳細用法見下表。

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成對使用。