現在的位置: 首頁 > 技術文章 > 基礎知識 > 正文

cortex-M3 的SVC、PendSV異常與RTOS

2018年08月25日 基礎知識 ⁄ 共 2958字 ⁄ 字號 cortex-M3 的SVC、PendSV異常與RTOS已關閉評論

SVC和PendSV

SVC(系統服務調用,亦簡稱系統調用)和PendSV(可懸起系統調用),它們多用于在操作系統之上的軟件開發中。

SVC:

SVC 用于產生系統函數的調用請求。

例如,操作系統不讓用戶程序直接訪問硬件,而是通過提供一些系統服務函數,用戶程序使用SVC 發出對系統服務函數的呼叫請求,以這種方法調用它們來間接訪問硬件。 因此, 當用戶程序想要控制特定的硬件時,它就會產生一個SVC 異常, 然后操作系統提供的SVC 異常服務例程得到執行, 它再調用相關的操作系統函數, 后者完成用戶程序請求的服務。

這種“提出要求——得到滿足”的方式,很好、很強大、很方便、很靈活、很能可持續發展。

首先,它使用戶程序從控制硬件的繁文縟節中解脫出來,而是由操作系統 負責控制具體的硬件。

第二,操作系統的代碼可以經過充分的測試,從而能使系統更加健壯和可靠。

第三,它使用戶程序無需在特權級下執行,用戶程序無需承擔因誤操作而癱瘓整個系統的風險。

第四,通過SVC 的機制,還讓用戶程序變得與硬件無關,因此在開發應用程序時無需了解硬件的操作細節,從而簡化了開發的難度和繁瑣度,并且使應用程序跨硬件平臺移植成為可能。開發應用程序唯一需要知道的就是操作系統提供的應用編程接口(API),并且了解各個請求代號和參數表,然后就可以使用SVC 來提出要求了(事實上,為使用方便,操作系統往往會提供一層封皮,以使系統調用的形式看起來和普通的函數調用一致。各封皮函數會正確使用SVC指令來執行系統調用——譯者注)。

其實,嚴格地講,操作硬件的工作是由設備驅動程序完成的,只是對應用程序來說,它們也是操作系統的一部分。如圖7.14 所示(點擊圖片可看大圖)

svc

SVC 異常通過執行”SVC”指令來產生。該指令需要一個立即數,充當系統調用代號。SVC異常服務例程稍后會提取出此代號,從而解釋本次調用的具體要求,再調用相應的服務函數。例如,

SVC 0x3 ; 調用3 號系統服務

在SVC 服務例程執行后,上次執行的SVC 指令地址可以根據自動入棧的返回地址計算出。找到了SVC 指令后,就可以讀取該SVC 指令的機器碼,從機器碼中萃取出立即數,就獲知了請求執行的功能代號。如果用戶程序使用的是PSP,服務例程還需要先執行

MRS Rn,PSP

指令來獲取應用程序的堆棧指針。通過分析LR 的值,可以獲知在SVC 指令執行時,正在使用哪個堆棧。
由CM3 的中斷優先級模型可知,你不能在SVC 服務例程中嵌套使用SVC 指令(事實上這樣做也沒意義),因為同優先級的異常不能搶占自身。這種作法會產生一個用法fault。同理,在NMI 服務例程中也不得使用SVC,否則將觸發硬fault。

PendSV:

另一個相關的異常是PendSV(可懸起的系統調用),它和SVC 協同使用。 一方面,SVC異常是必須立即得到響應的(若因優先級不比當前正處理的高,或是其它原因使之無法立即響應,將上訪成硬fault——譯者注),應用程序執行SVC 時都是希望所需的請求立即得到響應。 另一方面,PendSV 則不同,它是可以像普通的中斷一樣被搶占掛起的(不像SVC 那樣會上訪)。 操作系統 可以利用它“緩期執行”一個異常——直到其它重要的任務完成后才執行動作。

PendSV是什么?

根據 權威指南。PendSV是為系統設備而設的“可懸掛請求”(pendable request)。

  • 上下文切換 不能在中斷中進行,會導致中斷延期。為了解決這個問題,使用 PendSV。PendSV可以掛起,也就是等到別的 ISR結束后緩期執行。
  • 為了實現緩期執行PendSV,PendSV一定要被設置為最低優先級的異常。

掛起PendSV 的方法是:軟件實現OSIntCtxSw()函數,向NVIC 的PendSV 懸起寄存器中寫1。

NVIC_INT_CTRL EQU 0xE000ED04 ; Interrupt control state register.
NVIC_PENDSVSET EQU 0x10000000 ; Value to trigger PendSV exception.
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR

掛起后,如果優先級不夠高,則將緩期等待執行。

PendSV 的典型使用場合是在上下文切換時(在不同任務之間切換)。

操作系統,上下文切換 實例:

場景假設:一個系統(按時間片輪轉調度的系統)中有兩個就緒的任務(A任務、B任務),
上下文切換被觸發的場合可以是:

  • 執行一個系統調用
  • 系統滴答定時器(SYSTICK)中斷,(輪轉調度中需要)

A、B兩個就緒任務,通過SysTick 異常啟動上下文切換。如圖7.15 所示。(點擊圖片可看大圖)

systick

上圖是兩個任務輪轉調度的示意圖。 但若在產生SysTick 異常時正在響應一個中斷,則SysTick 異常會搶占其ISR。
在這種情況下,操作系統 不可以執行上下文切換,否則將使中斷請求被延遲, 而且在真實系統中延遲時間還往往不可預知——任何有一丁點實時要求的系統都決不能容忍這種事。 因此,在CM3 中也是嚴禁沒商量——如果操作系統 在某中斷活躍時嘗試切入線程模式,將觸犯用法fault 異常。(點擊圖片可看大圖)

irq

為解決此問題,早期的操作系統 大多會在SysTick 異常中 檢測當前是否有中斷在活躍中,只有沒有任何中斷需要響應時,才執行上下文切換(切換期間無法響應中斷)。

然而,這種方法的弊端在于, 它可能把任務切換動作拖延很久(因為如果搶占了IRQ,則本次SysTick 在執行后不得作上下文切換,只能等待下一次SysTick 異常),尤其是當某中斷源的頻率和SysTick 異常的頻率比較接近時,會發生“共振”。 現在好了,PendSV 來完美解決這個問題了(產生SysTick 異常時正在響應一個中斷,SysTick 異常會搶占其ISR。此時,操作系統 不可以執行上下文切換,否則將使中斷請求被延遲):

把PendSV 編程為最低優先級的異常,PendSV 異常會自動延遲上下文切換的請求,直到其它的ISR 都完成了處理后才放行。 如果操作系統 檢測到某IRQ 正在活動并且被SysTick 搶占,它將懸起一個PendSV 異常,以便緩期執行上下文切換。如圖7.17 所示(點擊圖片可看大圖)

pendsv

流水賬記錄如下:

1. 任務 A 呼叫SVC 來請求任務切換(例如,等待某些工作完成)

2. OS 接收到請求,做好上下文切換的準備,并且pend 一個PendSV 異常。

3. 當 CPU 退出SVC 后,它立即進入PendSV,從而執行上下文切換。

4. 當 PendSV 執行完畢后,將返回到任務B,同時進入線程模式。

5. 發生了一個中斷,并且中斷服務程序開始執行

6. 在 ISR 執行過程中,發生SysTick 異常,并且搶占了該ISR。

7. OS 執行必要的操作,然后pend 起PendSV 異常以作好上下文切換的準備。

8. 當 SysTick 退出后,回到先前被搶占的ISR 中,ISR 繼續執行

9. ISR 執行完畢并退出后,PendSV 服務例程開始執行,并且在里面執行上下文切換

10. 當 PendSV 執行完畢后,回到任務A,同時系統再次進入線程模式。

二八杠讨论心得
×