函數調用,中斷以及進程切換,的現場保護的區別

首先,函數調用是預料範圍內的代碼執行,是完全可控的,當前執行的函數調用另外一個函數時,是從當前代碼段通過跳轉指令主動跳轉到另外一個代碼段,只需保存跳轉之前的棧頂指針(fp),棧底指針(sp)到棧空間,保存跳轉指令的下一條指令的地址到lr寄存器,無須保存所有寄存器的值(如果C函數有使用R4-R11寄存器,還是要保存一下滴,這個C編譯器會幫我們完成,無需擔心,下面會詳細說明),然後還要使用寄存器r0-r3傳遞函數參數,或者使用棧空間傳遞函數參數。所以嚴格來說,函數調用過程中,不能嚴格說是現場保護。

(注:但是一個例外是 如果C語言調用匯編程序時,如果彙編中使用了R4-R11寄存器,在彙編的開頭和結尾要手動入站和出棧R4-R11寄存器,保護現場。如果,不保護R4-R11會有什麼影響,假設調用匯編後,C程序在調用匯編前將一個變量讀入到了 R4寄存器,C程序在調用匯編後如果要使用變量的值,可能直接從R4寄存器中取,而不是去內存中取,除非 此變量有 volatile修飾,所以彙編程序要保證不破壞R4-R11寄存器,因爲C的編譯器遵守這種C函數調用標準約定,所以編譯器默認以爲調用的彙編程序也遵守此約定,另外匯編程序不需保護R0-R3、R12寄存器,因爲編譯器認爲調用一個函數後,無論是彙編函數還是C函數,R0-R3、R12寄存器一定被破壞過了,所以編譯器不會像信任R4-R11寄存器那樣,在函數調用之後發生之後還去肆無忌憚地使用R0-R3、R12寄存器,再者說,在函數調用過程中,R0-R3擔負着傳遞參數和接收返回值的任務,所以一旦函數調用發生後,編譯器不再信任R0-R3、R12寄存器,但是仍然信任R4-R11,寄存器,所以彙編程序一定不能辜負編譯器的信任,一定要手動保護好R4-R11寄存器(C函數也會保護R4-R11,只不過C編譯器幫我們完成),至於R0-R3、R12寄存器,彙編程序可以隨意破壞。)

但是,中斷、進程切換這兩個過程和函數調用不一樣,中斷和進程切換實質都是靠中斷來完成的(系統調用或者系統滴答時鐘中斷來完成進程切換,但是普通中斷和進程切換又不完全相同),這裏就先簡單分析中斷過程。中斷是不可預測的,所以中斷產生時,硬件以及中斷服務程序中的代碼要保存所有的寄存器數據。我們以函數調用和中斷比較來說明爲什麼。假設函數調用前涉及到對一個局部變量的賦值操作,賦值操作編譯成彙編語句可能要至少兩句(假設是,把23 賦值給,0x10002100處的內存:mov r0, #23    str r0, [0x10002100]),那麼編譯器在編譯的時候,不可能在兩句彙編語句之間加上函數調用語句,肯定要等賦值完全完成後再進行函數調用相關語句,但是中斷就不同了,中斷可能隨時發生,可能在兩句賦值功能的語句中間出現,甚至可能在單獨一句彙編語句執行一半的時候出現(例如缺頁異常),那麼如果不保護現場,把所有可能在中斷服務程序中破話的寄存器全部保存下來,那麼中斷返回後賦值語句很可能執行失敗,所以,中斷髮生的時候,cpu硬件和中斷服務程序將相互配合,將中斷時刻所有的寄存器數據(嚴格來說也不是所有,是中斷服務函數可能破壞的寄存器)保存到棧空間(arm-linux是內核棧,cortex-m3單片機可能是內核棧也可能是用戶棧)

 

cortex-M3 中斷調用過程

  1. 入棧 
    中斷髮生後,中斷服務函數運行前,會先把xPSR, PC, LR, R12以及R3‐R0總共8個寄存器由硬件自動壓入適當的堆棧中(中斷前使用的MSP就壓入MSP,中斷前使用的是PSP就壓入PSP),爲啥袒護R0‐R3以及R12呢,R4‐R11就是下等公民?詳見C函數調用標準約定(《C/C++ Procedure Call Standard for the ARM Architecture》,AAPCS, Ref5),C函數實現的函數(不論是普通函數,還是中斷服務例程),一般都只用R0-R3、R12寄存器,並且在普通函數中這些寄存器被用來傳遞參數,所以C程序中斷服務函數會破壞R0-R3、R12,不會破壞R4-R11(即使破壞R4-R11,編譯器也會生成R4-R11的壓棧和出棧語句,還是相當於沒有破壞R4-R11),所以硬件爲了配合C函數實現的中斷服務函數,會在中斷服務函數運行之前,硬件壓棧R0-R3、R12寄存器,在中斷服務函數結束後,硬件出棧,這樣就保護了現場,C函數調用標準約定詳見https://blog.csdn.net/itismine/article/details/4752489 
                                                             入棧順序以及入棧後堆棧中的內容
  2. 取向量
    當數據總線(系統總線)正在爲入棧操作而忙得團團轉時,指令總線(I‐Code總線)可
    不是涼快地坐着看熱鬧——它正在爲響應中斷緊張有序地執行另一項重要的任務:從向量表
    中找出正確的異常向量,然後在服務程序的入口處預取指。由此可以看到各自都有專用總線
    的好處:入棧與取指這兩個工作能同時進行。
  3. 更新寄存器
    在入棧和取向量的工作都完畢之後,執行服務例程之前,還要更新一系列的寄存器:
    SP:在入棧中會把堆棧指針(PSP或MSP)更新到新的位置。在執行服務例程後,將由MSP負責對堆棧的訪問。
    PSR:IPSR位段(地處PSR的最低部分)會被更新爲新響應的異常編號。
    PC:在向量取出完畢後,PC將指向服務例程的入口地址,
    LR:LR的用法將被重新解釋,其值也被更新成一種特殊的值,稱爲“EXC_RETURN”,並且在異常返回時使用。EXC_RETURN的二進制值除了最低4位外全爲1,而其最低4位則有另外的含義
  4. 異常返回
    在CM3中,是通過把EXC_RETURN往PC裏寫來識別返回動作的。合法的EXC_RETURN值共3個
    詳見《Cortex-M3權威指南》
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章