1. 程式人生 > >RTX——第19章 SVC 中斷方式調用用戶函數(後期補歷程)

RTX——第19章 SVC 中斷方式調用用戶函數(後期補歷程)

相關 系統 call 指令 條件跳轉 mage fun sof 編程

本章節為大家講解如何采用 SVC 中斷方式調用用戶函數。 當用戶將 RTX 任務設置為工作在非特權級
模式時,任務中是不允許訪問特權級寄存器的,這個時候使用 SVC 中斷,此問題就迎刃而解了。
SVC 功能介紹
SVC 用於產生系統函數的調用請求。例如,操作系統通常不讓用戶程序直接訪問硬件,而是通過提供
一些系統服務函數,讓用戶程序使用 SVC 發出對系統服務函數的呼叫請求,以這種方法調用它們來間接
訪問硬件。因此,當用戶程序想要控制特定的硬件時,它就要產生一個 SVC 異常,然後操作系統提供的
SVC 異常服務例程得到執行,它再調用相關的操作系統函數,後者完成用戶程序請求的服務。
SVC 這種“提出要求——得到滿足”的方式很好:


? 它使用戶程序從控制硬件的繁文縟節中解脫出來,而是由 OS 負責控制具體的硬件。
? OS 的代碼可以經過充分的測試,從而能使系統更加健壯和可靠。
? 它使用戶程序無需在特權級下執行,用戶程序無需承擔因誤操作而癱瘓整個系統的風險。
? 通過 SVC 的機制,還讓用戶程序變得與硬件無關,因此在開發應用程序時無需了解硬件的操作細節,
從而簡化了開發的難度和繁瑣度,並且使應用程序跨硬件平臺移植成為可能。開發應用程序唯一需要
知道的就是操作系統提供的應用編程接口(API),並且在了解了各個請求代號和參數表後,就可以
使用 SVC 來提出要求了(事實上,為使用方便,操作系統往往會提供一層封皮,以使系統調用的形
式看起來和普通的函數調用一致。各封皮函數會正確使用 SVC 指令來執行系統調用)。其實,嚴格

地講,操作硬件的工作是由設備驅動程序完成的,只是對應用程序來說,它們也相當於操作系統的一
部分。如下圖所示:
技術分享

SVC 異常通過執行” SVC”指令來產生。該指令需要一個立即數,充當系統調用代號。 SVC 異常服務
例程稍後會提取出此代號,從而獲知本次調用的具體要求,再調用相應的服務函數。例如,
SVC 0x3 ; 調用 3 號系統服務
在 SVC 服務例程執行後,上次執行的 SVC 指令地址可以根據自動入棧的返回地址計算出。找到了 SVC
指令後,就可以讀取該 SVC 指令的機器碼,從機器碼中萃取出立即數,就獲知了請求執行的功能代號。
如果用戶程序使用的是 PSP,服務例程還需要先執行 MRS Rn, PSP 指令來獲取應用程序的堆棧指針。通


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

SVC 觸發方式
SVC 的異常號是 11,支持可編程。 SVC 異常可以由 SVC 指令來觸發,也可以通過 NVIC 來軟件觸發
通過寄存器 NVIC->STIR 觸發軟中斷)。這兩種方式觸發 SVC 中斷有一點不同:軟件觸發中斷是不精
確的,也就是說,搶占行為不一定會立即發生,即使當時它沒有被掩蔽,也沒有被其它 ISR 阻塞,也不能
保證馬上響應。這也是寫緩沖造成的,會影響到與操作 NVIC STIR 相臨的後一條指令:如果它需要根據中
斷服務的結果來決定如何工作(如條件跳轉),則該指令可能會誤動作——這也可以算是紊亂危象的一種
表現形式。為解決這個問題,必須使用一條 DSB 指令,如下例所示:
MOV R0, #SOFTWARE_INTERRUPT_NUMBER
LDR R1,=0xE000EF00 ; 加載 NVIC 軟件觸發中斷寄存器的地址
STR R0, [R1] ; 觸發軟件中斷
DSB ; 執行數據同步隔離指令
...
但是這種方式還有另一種隱患:如果欲觸發的軟件中斷被除能了,或者執行軟件中斷的程序自己也是個異
常服務程序,軟件中斷就有可能無法響應。因此,必須在使用前檢查這個中斷已經在響應中了。為達到此
目的,可以讓軟件中斷服務程序在入口處設置一個標誌。而 SVC 要精確很多,SVC 指令後,只要此時沒
有其它高優先級的異常也發生了,SVC 中斷服務程序可以得到立即執行。
SVC 的使用
SVC 是用於呼叫 OS 所提供的 API(RTX 是采用的這種方式)。用戶程序只需知道傳遞給 OS 的參數,
而不必知道各 API 函數的地址。 SVC 指令帶一個 8 位的立即數,可以視為是它的參數,被封裝在指令本身
中,如:
SVC 3 ;呼叫 3 號系統服務
則 3 被封裝在這個 SVC 指令中。因此在 SVC 服務例程中,需要讀取本次觸發 SVC 異常的 SVC 指令,並
提取出 8 位立即數所在的位段,來判斷系統調用號。
RTX 使用的 SVC 中斷服務號
SVC 的 0 號系統服務被 RTX 系統占用,即 SVC 0,用戶只能使用從 1 開始的服務號。而且用戶使
用的時候一定要保證從 1 開始,連續遞增使用,範圍 1 – 255。
RTX 中 SVC 中斷方式調用函數方法
用戶實現 SVC 中斷方式調用函數方法如下(下面以添加兩個 SVC 中斷為例):
? 第 1 步:添加 SVC_Table.s 文件。
我們在前面講解 RTX 的源碼移植方式時這個文件已經加上。
? 第 2 步:使用屬性__svc(x)聲明函數,x 從 1 開始,範圍 1-255。函數名隨便命名,但是 x 的數值一
定要保證是連續遞增的。
void __svc(1) SVC_1_FunCall(uint8_t _arg1, uint16_t _arg2, uint32_t _arg3, uint64_t *_arg4);
void __svc(2) SVC_2_FunCall(void);
? 第 3 步:寫上面兩個函數的實際代碼,並將函數名更改成__SVC_x 格式(統一改成這種命名方式是為
了跟 RTX 系統的調用方式__SVC_0 統一),x 是從 1 開始,範圍 1-255。 上面聲明的兩個函數不要動,
這裏修改的是實際函數名。 另外用戶可以根據需要加上中斷開關操作,因為 SVC 中斷可以被其它高
優先級的中斷搶占。
技術分享

技術分享

技術分享

技術分享

RTX——第19章 SVC 中斷方式調用用戶函數(後期補歷程)