百萬漢字注解 >> 精讀內核原始碼,中文注解分析, 深挖地基工程,大腦永久記憶,四大碼倉每日同步更新< gitee | github | csdn | coding >
百篇博客分析 >> 故事說內核,問答式導讀,生活式比喻,表格化說明,圖形化展示,多站點每日同步更新< oschina | csdn | weharmony >
本篇說清楚暫存器
ARM系列篇基于ARM720T.pdf檔案.
讀本篇之前建議先讀鴻蒙內核原始碼分析(總目錄)arm體系系列篇.
暫存器的本質
暫存器從大一的計算機組成原理就開始聽到它,感覺很神秘,如夢如霧多年.揭開本質后才發現,暫存器就是一個32位的存盤空間,一個int變數而已,但它的厲害之處在于極高頻率的使用,讓人不敢相信是怎么做到的,不管再復雜再牛牛的應用程式,電商也好,游戲,直播也罷,到了它這里都變成了有限的十幾個暫存器在玩,簡直太神奇了.
本篇將清楚說明暫存器的數量和功能,至于它是如何把復雜的上層程式變成了這十幾個暫存器來玩?這是編譯器的事情,不在討論范圍之內.
arm7有37個暫存器,具體看圖說明:

這些暫存器不能同時顯示,處理器指令狀態和作業模式指定哪些暫存器可供使用,圖中一一對應.
- 其中31個通用32位暫存器,系統和用戶模式全程復用暫存器,而其余5中例外(或叫特權)模式從R8_* ~ R14_* 的暫存器叫模式專屬暫存器.
- 注意 r8 和 r8_fiq是兩個不同的暫存器,名字前綴是為了好記,管理方便,以示同級概念理解.如此湊成了31個暫存器.
- 其中r13暫存器用于SP暫存器,始終指向堆疊頂,因為每種作業模式都有獨立的運行堆疊,所以有獨立的暫存器去記住各自的堆疊頂.
- 同理r14暫存器用于LR暫存器,用于保存模式切換時的切換位置,也是獨立存在,說明模式間回跳時并不需要重新給r14_*賦值,只需在跳出去的時候保存即可.
- 系統和用戶模式共用r13(sp)和r14(lr)暫存器,所以在每個子函式的堆疊幀中都要保存上一個呼叫它函式的SP和LR值,自己執行完成后要從堆疊幀中恢復這兩個暫存器的值,否則無法界定回去后從哪里開始,從哪里計算偏移位置.
- r15(pc)暫存器是指向代碼段的,所有模式復用的原因是它是共用的,一份代碼,你運行與不允許,代碼段就在哪里,不增不減.
- 6個狀態暫存器,其中CPSR(1個)和SPSR_*(5個),它們主要用于自運行或發生模式切換后的各種狀態保存.
- CPSR:程式狀態暫存器(current program status register) (當前程式狀態暫存器),在任何處理器模式下被訪問,
- SPSR:程式狀態保存暫存器(saved program status register),每一種處理器模式下都有一個狀態暫存器SPSR,SPSR用于保存CPSR的狀態,以便例外回傳后恢復例外發生時的作業狀態,當特定 的例外中斷發生時,這個暫存器用于存放當前程式狀態暫存器的內容,在例外中斷退出時,可以用SPSR來恢復CPSR,
七種作業模式
關于作業模式在 鴻蒙內核原始碼分析(總目錄)之作業模式篇中有詳細,可自行前往查看.此處只簡單說明下.
下圖來源于 ARM720T.pdf第43頁,在ARM體系中,CPU作業在以下七種模式中:

- 用戶模式(usr):屬于正常的用戶模式,ARM處理器正常的程式執行狀態,
- 快速中斷模式(fiq):用于處理快速中斷,對高速資料傳輸或通道處理
- 外部中斷模式(irq):對一般情況下的中斷進行處理,
- 管理模式(svc):屬于作業系統使用的保護模式,處理軟體中斷swi reset,
- 資料訪問終止模式(abt):當資料或指令預取終止時進入該模式,可用于處理存盤器故障、實作虛擬存盤器和存盤器保護,
- 系統模式(sys):運行具有特權的作業系統任務,
- 未定義指令中止模式(und):處理未定義的指令陷阱,當未定義的指令執行時進入該模式,可用于支持硬體協處理器的軟體仿真,
R0 暫存器
R0是暫存器中的王牌,被稱為頭號暫存器,通用暫存器中它用的最高頻,隨便翻段匯編代碼都能看到它的影子.鴻蒙開機第一跳指令就是 r0 = 0
reset_vector: //鴻蒙開機代碼
/* clear register TPIDRPRW */
mov r0, #0 @r0 = 0
mcr p15, 0, r0, c13, c0, 4 @c0,c13 = 0, C13為行程識別符號 含義見 ARM720T.PDF 第64頁
/* do some early cpu setup: i/d cache disable, mmu disabled */ @禁用MMU, i/d快取
mrc p15, 0, r0, c1, c0, 0 @r0 = c1 ,c1暫存器詳細解釋見第64頁
bic r0, #(1<<12) @位清除指令,清除r0的第11位
bic r0, #(1<<2 | 1<<0) @清除第0和2位 ,禁止 MMU和快取 0位:MMU enable/disable 2位:Cache enable/disable
mcr p15, 0, r0, c1, c0, 0 @c1=r0
再看拿自旋鎖的匯編代碼,這些代碼都在系列篇中詳細講解過,可前往鴻蒙內核原始碼分析(總目錄)自行查看.
FUNCTION(ArchSpinLock) @非要拿到鎖
mov r1, #1 @r1=1
1: @回圈的作用,因SEV是廣播事件.不一定lock->rawLock的值已經改變了
ldrex r2, [r0] @r0 = &lock->rawLock, 即 r2 = lock->rawLock
cmp r2, #0 @r2和0比較
wfene @不相等時,說明資源被占用,CPU核進入睡眠狀態
strexeq r2, r1, [r0]@此時CPU被重新喚醒,嘗試令lock->rawLock=1,成功寫入則r2=0
cmpeq r2, #0 @再來比較r2是否等于0,如果相等則獲取到了鎖
bne 1b @如果不相等,繼續進入回圈
dmb @用DMB指令來隔離,以保證緩沖中的資料已經落實到RAM中
bx lr @此時是一定拿到鎖了,跳回呼叫ArchSpinLock函式
R0被潛規則的干了兩件事,突出了它的重要性:
- 第一個引數 由R0保管,當然第二個引數就給R1保管
- 函式的回傳值統一交給R0保管, 例如 a -> b ,b執行完會把回傳值給r0,回到a后,a從r0取值,不管取到什么,它就認為這是b的回傳值,默認都只認r0保存了回傳值,這就是規定.
具體看一個C函式和它的匯編,系列篇也已經講過.
//++++++++++++ square(c -> 匯編)++++++++++++++++++++++++
int square(int a,int b){
return a*b;
}
square(int, int):
sub sp, sp, #8 @sp減去8,意思為給square分配堆疊空間,只用2個堆疊空間完成計算
str r0, [sp, #4] @第一個引數入堆疊
str r1, [sp] @第二個引數入堆疊
ldr r1, [sp, #4] @取出第一個引數給r1
ldr r2, [sp] @取出第二個引數給r2
mul r0, r1, r2 @執行a*b給R0,回傳值的作業一直是交給R0的
add sp, sp, #8 @函式執行完了,要釋放申請的堆疊空間
bx lr @子程式回傳,等同于mov pc,lr,即跳到呼叫處
//++++++++++++ fp(c -> 匯編)++++++++++++++++++++++++
int fp(int b)
{
int a = 1;
return square(a+b,a+b);
}
fp(int):
push {r11, lr} @r11(fp)/lr入堆疊,保存呼叫者main的位置
mov r11, sp @r11用于保存sp值,函式堆疊開始位置
sub sp, sp, #8 @sp減去8,意思為給fp分配堆疊空間,只用2個堆疊空間完成計算
str r0, [sp, #4] @先保存引數值,放在SP+4,此時r0中存放的是引數
mov r0, #1 @r0=1
str r0, [sp] @再把1也保存在SP的位置
ldr r0, [sp] @把SP的值給R0
ldr r1, [sp, #4] @把SP+4的值給R1
add r1, r0, r1 @執行r1=a+b
mov r0, r1 @r0=r1,用r0,r1傳參
bl square(int, int)@先mov lr, pc 再mov pc square(int, int)
mov sp, r11 @函式執行完了,要釋放申請的堆疊空間
pop {r11, lr} @彈出r11和lr,lr是專用標簽,彈出就自動復制給lr暫存器
bx lr @子程式回傳,等同于mov pc,lr,即跳到呼叫處
這段代碼同樣適用于理解一下的各個暫存器.
fp 暫存器
fp是堆疊幀暫存器(frame pointer),每個函式都有自己獨立的堆疊,fp標識從哪里開始,指向函式堆疊的堆疊底.
SP 暫存器
SP:堆疊指標暫存器(stack pointer),指向函式堆疊的堆疊頂,如此 fp 和 sp 就劃定了函式堆疊的范圍,函式在運行期間除了動態申請的記憶體要跑出去玩,其余就在這塊空間里玩.
LR 暫存器
例外的發生會導致程式正常運行的被打斷, 并將控制流轉移到相應的例外處理(例外回應),有些例外(fiq、irq)事件處理后,系統還希望能回 到當初例外發生時被打斷的源程式斷點處繼續完成源程式的執行(例外回傳),這就需要一種解決方案, 用于記錄源程式的斷點位置,以便正確的例外回傳,
類似的還有子程式的呼叫和 回傳,在主程式中(通過子程式呼叫指令)呼叫子程式時,也需要記錄下主程式中的呼叫點位置,以便將來的子程式的回傳,
LR:鏈接暫存器(linked pointer),就是用來解決上述問題的,ARM處理器中使用 R14實作對斷點和呼叫點的記錄,即R14用作回傳連接暫存器(LR),確保回來知道自己從哪個位置中斷,以便繼續執行.
PC 暫存器
pc:程式計數暫存器(Program Counter Register) 向代碼段取指令,指向代碼段具體位置,PC暫存器涉及到arm的流水線結構設計,具體在后續流水線篇中詳細說明,敬請關注.
CPSR 暫存器

CPSR(current program status register)當前程式的狀態暫存器
CPSR有4個8位區域:標志域(F)、狀態域(S)、擴展域(X)、控制域(C)
32 位的程式狀態暫存器可分為4 個域:
-
- 位[31:24]為條件標志位域,用f 表示;
-
- 位[23:16]為狀態位域,用s 表示;
-
- 位[15:8]為擴展位域,用x 表示;
-
- 位[7:0]為控制位域,用c 表示;
CPSR和其他暫存器不一樣,其他暫存器是用來存放資料的,都是整個暫存器具有一個含義.
而CPSR暫存器是按位起作用的,也就是說,它的每一位都有專門的含義,記錄特定的資訊.
CPSR的低8位(包括I、F、T和M[4:0])稱為控制位,程式無法修改,
除非CPU運行于特權模式下,程式才能修改控制位
N、Z、C、V均為條件碼標志位,它們的內容可被算識訓邏輯運算的結果所改變,
并且可以決定某條指令是否被執行!意義重大!
- CPSR的第31位是 N,符號標志位,它記錄相關指令執行后,其結果是否為負.
如果為負 N = 1,如果是非負數 N = 0. - CPSR的第30位是Z,0標志位,它記錄相關指令執行后,其結果是否為0.
如果結果為0.那么Z = 1.如果結果不為0,那么Z = 0. - CPSR的第29位是C,進位標志位(Carry),一般情況下,進行無符號數的運算,
加法運算:當運算結果產生了進位時(無符號數溢位),C=1,否則C=0,
減法運算(包括CMP):當運算時產生了借位時(無符號數溢位),C=0,否則C=1, - CPSR的第28位是V,溢位標志位(Overflow),在進行有符號數運算的時候,
如果超過了機器所能標識的范圍,稱為溢位,
MSR{條件} 程式狀態暫存器(CPSR 或SPSR)_<域>,運算元
MSR 指令用于將運算元的內容傳送到程式狀態暫存器的特定域中
示例如下:
MSR CPSR,R0 @傳送R0 的內容到CPSR
MSR SPSR,R0 @傳送R0 的內容到SPSR
MSR CPSR_c,R0 @傳送R0 的內容到CPSR,但僅僅修改CPSR中的控制位域
MRS{條件} 通用暫存器,程式狀態暫存器(CPSR 或SPSR)
MRS 指令用于將程式狀態暫存器的內容傳送到通用暫存器中,該指令一般用在以下兩種情況:
1) 當需要改變程式狀態暫存器的內容時,可用MRS 將程式狀態暫存器的內容讀入通用暫存器,修改后再寫回程式狀態暫存器,
2) 當在例外處理或行程切換時,需要保存程式狀態暫存器的值,可先用該指令讀出程式狀態暫存器的值,然后保存,
示例如下:
MRS R0,CPSR @傳送CPSR 的內容到R0
MRS R0,SPSR @傳送SPSR 的內容到R0
@MRS指令是唯一可以直接讀取CPSR和SPSR暫存器的指令
SPSR 暫存器
SPSR(saved program status register)程式狀態保存暫存器.五種例外模式下一個狀態暫存器SPSR,用于保存CPSR的狀態,以便例外回傳后恢復例外發生時的作業狀態,
- 1、SPSR 為 CPSR 中斷時刻的副本,退出中斷后,將SPSR中資料恢復到CPSR中,
- 2、用戶模式和系統模式下SPSR不可用,所以SPSR暫存器只有5個
喜歡就大方 點贊+關注+收藏 吧
各大站點搜 “鴻蒙內核原始碼分析” ,快速找到組織.

百萬漢字注解 >> 精讀內核原始碼,中文注解分析, 深挖地基工程,大腦永久記憶,四大碼倉每日同步更新< gitee | github | csdn | coding >
百篇博客分析 >> 故事說內核,問答式導讀,生活式比喻,表格化說明,圖形化展示,多站點每日同步更新< oschina | csdn | weharmony >
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/265912.html
標籤:其他
上一篇:計算機網路基礎考點筆記-1
