精讀內(nèi)核源碼就繞不過匯編語言,鴻蒙內(nèi)核有6個(gè)匯編文件,讀不懂它們就真的很難理解以下問題.
1.系統(tǒng)調(diào)用是如何實(shí)現(xiàn)的?
2.CPU是如何切換任務(wù)和進(jìn)程上下文的?
3.硬件中斷是如何處理的?
4.main函數(shù)到底是怎么來的?
5.開機(jī)最開始發(fā)生了什么?
6.關(guān)機(jī)最后的最后又發(fā)生了什么?
以下是一個(gè)很簡(jiǎn)單的C文件編譯成匯編代碼后的注解.讀懂這些注解會(huì)發(fā)現(xiàn)匯編很可愛,甚至還會(huì)上癮,并沒有想象中的那么恐怖,讀懂它會(huì)顛覆你對(duì)匯編和棧的認(rèn)知.
#include#include int square(int a,int b){ return a*b; } int fp(int b) { int a = 1; return square(a+b,a+b); } int main() { int sum = 1; for(int a = 0;a < 100; a++){ sum = sum + fp(a); } return sum; }
//編譯器: armv7-a clang (trunk) square(int, int): sub sp, sp, #8 @sp減去8,意思為給square分配棧空間,只用2個(gè)棧空間完成計(jì)算 str r0, [sp, #4] @第一個(gè)參數(shù)入棧 str r1, [sp] @第二個(gè)參數(shù)入棧 ldr r1, [sp, #4] @取出第一個(gè)參數(shù)給r1 ldr r2, [sp] @取出第二個(gè)參數(shù)給r2 mul r0, r1, r2 @執(zhí)行a*b給R0,返回值的工作一直是交給R0的 add sp, sp, #8 @函數(shù)執(zhí)行完了,要釋放申請(qǐng)的棧空間 bx lr @子程序返回,等同于mov pc,lr,即跳到調(diào)用處 fp(int): push {r11, lr} @r11(fp)/lr入棧,保存調(diào)用者main的位置 mov r11, sp @r11用于保存sp值,函數(shù)棧開始位置 sub sp, sp, #8 @sp減去8,意思為給fp分配棧空間,只用2個(gè)棧空間完成計(jì)算 str r0, [sp, #4] @先保存參數(shù)值,放在SP+4,此時(shí)r0中存放的是參數(shù) 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 @執(zhí)行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 @函數(shù)執(zhí)行完了,要釋放申請(qǐng)的棧空間 pop {r11, lr} @彈出r11和lr,lr是專用標(biāo)簽,彈出就自動(dòng)復(fù)制給lr寄存器 bx lr @子程序返回,等同于mov pc,lr,即跳到調(diào)用處 main: push {r11, lr} @r11(fp)/lr入棧,保存調(diào)用者的位置 mov r11, sp @r11用于保存sp值,函數(shù)棧開始位置 sub sp, sp, #16 @sp減去8,意思為給main分配棧空間,只用2個(gè)棧空間完成計(jì)算 mov r0, #0 @初始化r0 str r0, [r11, #-4] @作用是保存SUM的初始值 str r0, [sp, #8] @sum將始終占用SP+8的位置 str r0, [sp, #4] @a將始終占用SP+4的位置 b .LBB1_1 @跳到循環(huán)開始位置 .LBB1_1: @循環(huán)開始位置入口 ldr r0, [sp, #4] @取出a的值給r0 cmp r0, #99 @跟99比較 bgt .LBB1_4 @大于99,跳出循環(huán) mov pc .LBB1_4 b .LBB1_2 @繼續(xù)循環(huán),直接 mov pc .LBB1_2 .LBB1_2: @符合循環(huán)條件入口 ldr r0, [sp, #8] @取出sum的值給r0,sp+8用于寫SUM的值 str r0, [sp] @先保存SUM的值,SP的位置用于讀SUM值 ldr r0, [sp, #4] @r0用于傳參,取出A的值給r0作為fp的參數(shù) bl fp(int) @先mov lr, pc再mov pc fp(int) mov r1, r0 @fp的返回值為r0,保存到r1 ldr r0, [sp] @取出SUM的值 add r0, r0, r1 @計(jì)算新sum的值,由R0保存 str r0, [sp, #8] @將新sum保存到SP+8的位置 b .LBB1_3 @無條件跳轉(zhuǎn),直接 mov pc .LBB1_3 .LBB1_3: @完成a++操作入口 ldr r0, [sp, #4] @SP+4中記錄是a的值,賦給r0 add r0, r0, #1 @r0增加1 str r0, [sp, #4] @把新的a值放回SP+4里去 b .LBB1_1 @跳轉(zhuǎn)到比較 a < 100 處 .LBB1_4: @循環(huán)結(jié)束入口 ldr r0, [sp, #8] @最后SUM的結(jié)果給R0,返回值的工作一直是交給R0的 mov sp, r11 @函數(shù)執(zhí)行完了,要釋放申請(qǐng)的棧空間 pop {r11, lr} @彈出r11和lr,lr是專用標(biāo)簽,彈出就自動(dòng)復(fù)制給lr寄存器 bx lr @子程序返回,跳轉(zhuǎn)到lr處等同于 MOV PC, LR
這個(gè)簡(jiǎn)單的匯編并不是鴻蒙的匯編,只是先打個(gè)底,由淺入深, 但看懂了它基本理解鴻蒙匯編代碼沒有問題, 后續(xù)將詳細(xì)分析鴻蒙內(nèi)核各個(gè)匯編文件的作用. 開始分析上面的匯編代碼.
第一:上面的代碼和鴻蒙內(nèi)核用棧方式一樣,都采用了遞減滿棧的方式, 什么是遞減滿棧? 遞減指的是棧底地址高于棧頂?shù)刂?滿棧指的是SP指針永遠(yuǎn)在棧頂.一定要理解遞減滿棧,否則讀不懂內(nèi)核匯編代碼.舉例說明:
square(int, int): sub sp, sp, #8 @sp減去8,意思為給square分配棧空間,只用2個(gè)棧空間完成計(jì)算 str r0, [sp, #4] @第一個(gè)參數(shù)入棧 str r1, [sp] @第二個(gè)參數(shù)入棧 ldr r1, [sp, #4] @取出第一個(gè)參數(shù)給r1 ldr r2, [sp] @取出第二個(gè)參數(shù)給r2 mul r0, r1, r2 @執(zhí)行a*b給R0,返回值的工作一直是交給R0的 add sp, sp, #8 @函數(shù)執(zhí)行完了,要釋放申請(qǐng)的棧空間 bx lr @子程序返回,等同于mov pc,lr,即跳到調(diào)用處
首句匯編的含義就是申請(qǐng)棧空間,sp = sp - 8,一個(gè)棧內(nèi)單元(棧空間)占4個(gè)字節(jié),申請(qǐng)2個(gè)棧空間搞定函數(shù)的計(jì)算,仔細(xì)看下代碼除了在函數(shù)的末尾sp = sp + 8又恢復(fù)在之前的位置的中間過程,SP的值是沒有任務(wù)變化,它的指向是不動(dòng)的, 這跟很多人對(duì)棧的認(rèn)知是不一樣的,它只是被用于計(jì)算,例如ldr r1, [sp, #4]的意思是取出SP+4這個(gè)虛擬地址的值給r1寄存器,SP的值并沒有改變的,為什么要+呢,因?yàn)镾P是指向棧頂?shù)?地址是最小的. 滿棧就是用棧過程中對(duì)地址的操作不能超過SP,所以你很少在計(jì)算過程中看到 把sp-4地址中的值給某個(gè)寄存器, 除非是特別的指令,否則不可能有這樣的指令.
第二:sub sp, sp, #8和add sp, sp, #8是成對(duì)出現(xiàn)的,這就跟申請(qǐng)內(nèi)存,釋放內(nèi)存的道理一樣,這是內(nèi)核對(duì)任務(wù)的運(yùn)行棧管理方式,一樣用多少申請(qǐng)多少,用完釋放.空間大小就是棧幀,這是棧幀的本質(zhì)含義.
第三:push {r11, lr}和pop {r11, lr}也是成對(duì)出現(xiàn)的,主要是用于函數(shù)調(diào)用,例如 A -> B, B要保存A的棧幀范圍和指令位置, lr保存是是A函數(shù)執(zhí)行到哪個(gè)指令的位置, r11干了fp的工作,其實(shí)就是指向 A的棧頂位置,如此B執(zhí)行完后return回A的時(shí)候,先mov pc,lr 內(nèi)核就知道改執(zhí)行A的哪條指令了,同時(shí)又知道了A的棧頂位置.
第四:頻繁出現(xiàn)的R0寄存器的作用用于傳參和返回值, A調(diào)用B之前,假如有兩個(gè)參數(shù),就把參數(shù)給r0 ,r1記錄,充當(dāng)了A的變量, 到了B中后,先讓r0,r1入棧,目的是保存參數(shù)值, 因?yàn)?B中要用r0,r1,他們變成B的變量用了. 返回值都是默認(rèn)統(tǒng)一給r0保存. B中將返回值給r0,回到A中取出R0值對(duì)A來說這就是B的返回值.
這是以上為匯編代碼的分析,追問兩個(gè)問題
第一:如果是可變參數(shù)怎么辦? 100個(gè)參數(shù)怎么整, 通過寄存器總共就12個(gè),不夠傳參啊 第二:返回值可以有多個(gè)嗎?
編輯:hfy
-
寄存器
+關(guān)注
關(guān)注
31文章
5343瀏覽量
120368
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論