RT-thread內核之異常與中斷


一、什么是中斷?

中斷有兩種,一種是CPU本身在執行程序的過程中產生的,一種是由CPU外部產生的。 cpu外部中斷,就是通常所講的“中斷”(interrupt)。對於執行程序來說,這種“中斷”的發生完全是異步的,因為不知道什么時候會發生。CPU對其的響應也完全是被動的, 可以通過“關中斷”指令關閉對其的響應。 然而由軟件產生的中斷一般是由專設的指令,如X86中的“INT n”在程序中有意產生的, 是主動的,同步的。只要CPU執行一條INT指令,在開始執行下一條指令之前一定會進入中 斷服務程序。這種主動的中斷稱為“陷阱”(trap)

從物理學的角度看,中斷是一種電信號,由硬件設備產生,並直接送入中斷控制器的輸入引腳上,然后再由中斷控制器向處理器發送相應的信號。處理器一經檢測到該信號,便中斷自己當前正在處理的工作,轉而去處理中斷。此后,處理器會通知 OS 已經產生中斷。這樣,OS 就可以對這個中斷進行適當的處理。不同的設備對應的中斷不同,而每個中斷都通過一個唯一的數字標識,這些值通常被稱為中斷請求線(IRQ)。
中斷可分為同步(synchronous)中斷和異步(asynchronous)中斷:

1. 同步中斷是當指令執行時由 CPU 控制單元產生,之所以稱為同步,是因為只有在一條指令執行完畢后 CPU 才會發出中斷,而不是發生在代碼指令執行期間,比如系統調用。

2. 異步中斷是指由其他硬件設備依照 CPU 時鍾信號隨機產生,即意味着中斷能夠在指令之間發生,例如鍵盤中斷。

二、什么是異常?

同步中斷又稱為異常(exception);異步中斷則被稱為中斷(interrupt)。我們通常講的中斷指的都是異步中斷,即cpu外部中斷。
1.中斷可分為可屏蔽中斷(Maskable interrupt)和非屏蔽中斷(Nomaskable interrupt)。
2.異常可分為故障(fault)、陷阱(trap)、終止(abort)三類。

這些類別之間的異同點如下:

類別             原因                   異步/同步                   返回行為
中斷      來自I/O設備的信號          異步                 總是返回到下一條指令
陷阱      有意的異常                    同步                 總是返回到下一條指令
故障      潛在可恢復的錯誤           同步                 返回到當前指令
終止      不可恢復的錯誤              同步                 不會返回

三、中斷處理過程

當中斷產生時,處理器將按如下的順序執行:
• 保存當前處理機狀態信息
• 載入異常或中斷處理函數到PC寄存器
• 把控制權轉交給處理函數並開始執行
• 當處理函數執行完成時,恢復處理器狀態信息
• 從異常或中斷中返回到前一個程序執行點

中斷使得CPU可以在事件發生時才予以處理,而不必讓CPU連續不斷地查詢是否有相應的事件發生。通過兩條特殊指令:關中斷和開中斷可以讓處理器不響應或響應中斷(在關閉中斷期間,通常處理器會把新產生的中斷掛起,當中斷打開時立刻進行響應)。在執行中斷服務例程的過程中,如果有更高優先級別的中斷源觸發中斷,由於當前處於中斷處理上下文環境中,根據不同的處理器構架可能有不同的處理方式:比如新的中斷等待掛起直到當前中斷處理離開后再行響應,但這在硬實時環境中不允許發生;或者新的高優先級中斷打斷當前中斷處理過程,而去直接響應這個更高優先級的新中斷源,這稱為中斷嵌套,而中斷是否能夠嵌套,一般由MCU處理器的中斷機制決定,如stm32中只有新中斷的搶占優先級比當前中斷高時,才能打斷當前中斷進入新的中斷服務函數。

在系統響應中斷前,軟件代碼(或處理器)需要把當前線程的上下文保存下來(通常保存在當前線程的線程棧中),再調用中斷服務例程進行中斷響應、處理。在進行中斷處理時(實質是調用用戶的中斷服務例程函數),中斷處理函數中很可能會有自己的局部變量,這些都需要相應的棧空間來保存,所以中斷響應依然需要一個棧空間來做為上下文運行中斷處理函數。中斷棧可以保存在打斷的線程棧中,當從中斷中退出時,返回相應的線程繼續執行。中斷棧也可以與打斷線程棧完全分離開來,即每次進入中斷時,在保存完被打斷的線程上下文后,切換到新的中斷棧中獨立運行;在中斷退出時,再做相應的上下文恢復。

使用獨立中斷棧相對來說更容易實現,並且對於線程棧使用情況也比較容易了解掌握(否則必須要為中斷棧預留空間,如果系統支持中斷嵌套,還需要考慮應該為嵌套中斷預留多大的空間)。RT-Thread采用的方式是提供獨立的中斷棧,即中斷發生時,中斷的前期處理程序會將用戶的棧指針更換到系統事先留出的中斷棧空間中,等中斷退出時再恢復用戶的棧指針。這樣中斷就不會占用線程的棧空間,從而提高了內存空間的利用率,且隨着任務的增加,這種減少內存占用的的效果也越明顯。以stm32的cotex-M3/M4為例,cotex-M3/M4中擁有兩個堆棧指針,然而它們是banked,因此任一時刻只能使用其中的一個:

      主堆棧指針(MSP):復位后缺省使用的堆棧指針,用於操作系統內核,以及異常與中斷處理。

      進程堆棧指針(PSP):由用戶的應用程序代碼(如線程切換)使用。堆棧指針的最低兩位永遠是0,這意味着堆棧總是4 字節對齊的。

由此可以看出,在一個實際運行系統里有兩大部分:一是操作系統和中斷,一是用戶應用程序。它們使用的資源是不一樣的,從中斷(線程調度時產生的中斷)返回到用戶應用程序線程時,系統使用的堆棧指針也從MSP變成PSP。

四、與操作系統相關的中斷接口:在src/irq.c中

void rt_interrupt_enter(void);
void rt_interrupt_leave(void); 
當整個系統被中斷打斷,進入中斷處理函數時,OS需要知道當前已經進入到中斷狀態。
rt_interrupt_enter函數用於通知OS,當前已經進入了中斷狀態;rt_interrupt_leave函數用於通知OS,已經離開中斷狀態。通常來說,OS需要知道這樣的運行狀態,這樣在中斷服務例程中,如果調用了OS相關的調用,OS好及時調整相應的行為,例如進行任務切換時應該采取中斷中任務切換的策略,而不是立即進行切換。但是如果中斷服務例程很顯然、很必然地不會去調用OS相關的函數,此時也可以不調用rt_interrupt_enter/leave函數。

rt_uint8_t rt_interrupt_get_nest(void);
獲取當前中斷嵌套計數值,大於0說明處於中斷服務中,大於1說明存在中斷嵌套

五、與MCU相關的中斷接口:以stm32f4為例

關閉中斷:在libcpu/arm/cotex-m4/context_rvds.S中用匯編語言實現
rt_base_t rt_hw_interrupt_disable(void);
函數返回中斷前的系統中斷狀態。
當系統關閉了中斷時,就意味着當前線程/代碼不會被其他事件所打斷(因為整個系統已經不再對外部事件響應),也就是當前線程不會被搶占(因為線程切換時用到了PendSV_Handler),除非這個線程主動讓出處理器。

打開中斷:在libcpu/arm/cotex-m4/context_rvds.S中用匯編語言實現
void rt_hw_interrupt_enable(rt_base_t level);
調用這個函數接口將恢復調用rt_hw_interrupt_disable前的中斷狀態,level是上一次關閉中斷時返回的值。打開中斷往往是和關閉中斷成對使用的,用於恢復關閉中斷前的狀態。
注意:調用這個接口並不代表着肯定打開中斷,而是恢復關閉中斷前的狀態,如果調用rt_hw_interrupt_disable()前是關中斷狀態,那么調用此函數后依然是關中斷狀態。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM