FreeRTOS 臨界段和開關中斷
臨界段
代碼的臨界段也稱為臨界區,一旦這部分代碼開始執行,則不允許任何中斷打斷。為確保臨界段代碼
的執行不被中斷,在進入臨界段之前須關中斷,而臨界段代碼執行完畢後,要立即開中斷。
FreeRTOS 臨界段相關知識補充
FreeRTOS 的源碼中有多處臨界段的地方, 臨界段雖然保護了關鍵代碼的執行不被打斷, 但也會
影響系統的實時性。比如此時某個任務正在調用系統 API 函數,而且此時中斷正好關閉了,也就是進
入到了臨界區中,這個時候如果有一個緊急的中斷事件被觸發,這個中斷就不能得到及時執行,必須
等到中斷開啟才可以得到執行, 如果關中斷時間超過了緊急中斷能夠容忍的限度, 危害是可想而知的。
FreeRTOS 源碼中就有多處臨界段的處理,跟 FreeRTOS 一樣,uCOS-II 和 uCOS-III 源碼中都是有
臨界段的,而 RTX 的源碼中不存在臨界段。 另外,除了 FreeRTOS 操作系統源碼所帶的臨界段以外,用
戶寫應用的時候也有臨界段的問題,比如以下兩種:
? 讀取或者修改變量(特別是用於任務間通信的全局變量)的代碼,一般來說這是最常見的臨界代碼。
? 調用公共函數的代碼,特別是不可重入的函數,如果多個任務都訪問這個函數,結果是可想而知的。
總之,對於臨界段要做到執行時間越短越好,否則會影響系統的實時性。
任務代碼臨界段處理
FreeRTOS 任務代碼中臨界段的進入和退出主要是通過操作寄存器 basepri 實現的。進入臨界段前操
作寄存器 basepri 關閉了所有小於等於宏定義 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
所定義的中斷優先級,這樣臨界段代碼就不會被中斷幹擾到,而且實現任務切換功能的 PendSV 中斷和滴
答定時器中斷是最低優先級中斷,所以此任務在執行臨界段代碼期間是不會被其它高優先級任務打斷的。
退出臨界段時重新操作 basepri 寄存器,即打開被關閉的中斷(這裏我們不考慮不受 FreeRTOS 管理的更
高優先級中斷)。 FreeRTOS 進入和退出臨界段的函數如下:
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
上面這兩個函數是供用戶調用的,其中函數 taskENTER_CRITICAL 是進入臨界段,函數
taskEXIT_CRITICAL 是退出臨界段。 進一步跟蹤宏定義的實現如下:
#define portENTER_CRITICAL() vPortEnterCritical()
#define portEXIT_CRITICAL() vPortExitCritical()
再進一步跟蹤宏定義的實現如下:
通過上面的兩個函數 vPortEnterCritical 和 vPortExitCritical 可以看出,進入臨界段和退出臨界段是通過
函數調用開關中斷函數 portENABLE_INTERRUPTS 和 portDISABLE_INTERRUPTS 實現的。 細心的讀者
還會發現上面的這兩個函數都對變量 uxCriticalNesting 進行了操作。這個變量比較重要,用於臨界段的
嵌套計數。初學的同學也許會問這裏直接的開關中斷不就可以了嗎,為什麽還要做一個嵌套計數呢?主要
是因為直接的開關中斷方式不支持在開關中斷之間的代碼裏再次執行開關中斷的嵌套處理,假如當前我們
的代碼是關閉中斷的,嵌套了一個含有開關中斷的臨界區代碼後,退出時中斷就成開的了,這樣就出問題
了。 通過嵌套計數就有效地防止了用戶嵌套調用函數 taskENTER_CRITICAL 和 taskEXIT_CRITICAL 時出錯。
FreeRTOS 臨界段和開關中斷