鴻蒙內(nèi)核源碼分析(匯編基礎(chǔ)篇) | CPU在哪里打卡上班

本篇通過拆解一段很簡單的匯編代碼來快速認識匯編,為讀懂鴻蒙匯編打基礎(chǔ).系列篇后續(xù)將逐個剖析鴻蒙的匯編文件.

匯編很簡單

  • 第一: 要認定匯編語言一定是簡單的,沒有高深的東西,無非就是數(shù)據(jù)的搬來搬去,運行時數(shù)據(jù)主要待在兩個地方:內(nèi)存和寄存器。寄存器是CPU內(nèi)部存儲器,離運算器最近,所以最快.

  • 第二: 運行空間(棧空間)就是CPU打卡上班的地方,內(nèi)核設(shè)計者規(guī)定誰請CPU上班由誰提供場地,用戶程序提供的場地叫用戶棧,敏感工作CPU要帶回公司做,公司提供的場地叫內(nèi)核棧,敏感工作叫系統(tǒng)調(diào)用,系統(tǒng)調(diào)用的本質(zhì)理解是CPU要切換工作模式即切換辦公場地。

  • 第三:CPU的工作順序是流水線的,它只認指令,而且只去一個地方(指向代碼段的PC寄存器)拿指令運算消化。指令集是告訴外界我CPU能干什么活并提供對話指令,匯編語言是人和CPU能愉快溝通不擰巴的共識語言。一一對應(yīng)了CPU指令,又能確保記性不好的人類能模塊化的設(shè)計idea, 先看一段C編譯成匯編代碼再來說模塊化。

square(c -> 匯編)

//編譯器: armv7-a clang (trunk)
//++++++++++++ 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]   @第一個參數(shù)入棧
        str     r1, [sp]       @第二個參數(shù)入棧
        ldr     r1, [sp, #4]   @取出第一個參數(shù)給r1
        ldr     r2, [sp]       @取出第二個參數(shù)給r2
        mul     r0, r1, r2     @執(zhí)行a*b給R0,返回值的工作一直是交給R0的
        add     sp, sp, #8     @函數(shù)執(zhí)行完了,要釋放申請的棧空間
        bx      lr             @子程序返回,等同于mov pc,lr,即跳到調(diào)用處

fp(c -> 匯編)

//++++++++++++ fp(c -> 匯編)++++++++++++++++++++++++
int fp(int b)
{
    int a = 1;
    return square(a+b,a+b);
}
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個棧空間完成計算
        str     r0, [sp, #4]   @先保存參數(shù)值,放在SP+4,此時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í)行完了,要釋放申請的棧空間 
        pop     {r11, lr}      @彈出r11和lr,lr是專用標(biāo)簽,彈出就自動復(fù)制給lr寄存器
        bx      lr             @子程序返回,等同于mov pc,lr,即跳到調(diào)用處

main(c -> 匯編)

//++++++++++++ main(c -> 匯編)++++++++++++++++++++++++
int main()
{
    int sum = 0;
    for(int a = 0;a < 100; a++){
        sum = sum + fp(a);
    }
    return sum;
}
main:
        push    {r11, lr}      @r11(fp)/lr入棧,保存調(diào)用者的位置
        mov     r11, sp        @r11用于保存sp值,函數(shù)棧開始位置
        sub     sp, sp, #16    @sp減去16,意思為給main分配棧空間,只用4個棧空間完成計算
        mov     r0, #0         @初始化r0
        str     r0, [r11, #-4] @執(zhí)行sum = 0
        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     @計算新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í)行完了,要釋放申請的棧空間
        pop     {r11, lr}      @彈出r11和lr,lr是專用標(biāo)簽,彈出就自動復(fù)制給lr寄存器
        bx      lr             @子程序返回,跳轉(zhuǎn)到lr處等同于 MOV PC, LR

代碼有點長,都加了注釋,如果能直接看懂那么恭喜你,鴻蒙內(nèi)核的6個匯編文件基于也就懂了。這是以下C文件全貌

文件全貌

#include <stdio.h>
#include <math.h>

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 = 0;
    for(int a = 0;a < 100; a++){
        sum = sum + fp(a);
    }
    return sum;
}

代碼很簡單誰都能看懂,代碼很典型,具有代表性,有循環(huán),有判斷,有運算,有多級函數(shù)調(diào)用。編譯后的匯編代碼基本和C語言的結(jié)構(gòu)差不太多,區(qū)別是對循環(huán)的實現(xiàn)用了四個模塊,四個模塊也好理解:
一個是開始塊(LBB1_1), 一個符合條件的處理塊(LBB1_2),一個條件發(fā)生變化塊(LBB1_3),最后收尾塊(LBB1_4).

按塊逐一剖析.

先看最短的那個

int square(int a,int b){
    return a*b;
}
//編譯成
square(int, int):
        sub     sp, sp, #8     @sp減去8,意思為給square分配棧空間,只用2個棧空間完成計算
        str     r0, [sp, #4]   @第一個參數(shù)入棧
        str     r1, [sp]       @第二個參數(shù)入棧
        ldr     r1, [sp, #4]   @取出第一個參數(shù)給r1
        ldr     r2, [sp]       @取出第二個參數(shù)給r2
        mul     r0, r1, r2     @執(zhí)行a*b給R0,返回值的工作一直是交給R0的
        add     sp, sp, #8     @函數(shù)執(zhí)行完了,要釋放申請的棧空間
        bx      lr             @子程序返回,等同于mov pc,lr,即跳到調(diào)用處

首先上來一句 sub sp, sp, #8 等同于 sp = sp - 8 ,CPU運行需要場地,這個場地就是棧 ,SP是指向棧的指針,表示此時用棧的刻度. 代碼和鴻蒙內(nèi)核用棧方式一樣,都采用了遞減滿棧的方式(FD).

什么是遞減滿棧? 遞減指的是棧底地址高于棧頂?shù)刂罚瑮5纳L方向是遞減的, 滿棧指的是SP指針永遠指向棧頂. 每個函數(shù)都有自己獨立的棧底和棧頂,之間的空間統(tǒng)稱棧幀.可以理解為分配了一塊

區(qū)域給函數(shù)運行,sub sp, sp, #8 代表申請2個棧空間,一個棧空間按四個字節(jié)算.

用完要不要釋放?當(dāng)然要,add sp, sp, #8 就是釋放棧空間. 是一對的,減了又加回去,空間就歸還了.

ldr r1, [sp, #4] 的意思是取出SP+4這個虛擬地址的值給r1寄存器,而SP的指向并沒有改變的,還是在棧頂, 為什么要+呢, +就是往回數(shù), 定位到分配的棧空間上.

一定要理解遞減滿棧,這是關(guān)鍵! 否則讀不懂內(nèi)核匯編代碼.

入?yún)⒎绞?/h3>

一般都是通過寄存器(r0..r10)傳參,fp調(diào)用square之前會先將參數(shù)給(r0..r10)

        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) 

到了square中后,先讓 r0,r1入棧,目的是保存參數(shù)值, 因為 square中要用r0,r1 ,

        str     r0, [sp, #4]   @先入棧保存第一個參數(shù)
        str     r1, [sp]       @再入棧保存第二個參數(shù)
        ldr     r1, [sp, #4]   @再取出第一個參數(shù)給r1,(a*b)中a值
        ldr     r2, [sp]       @再取出第二個參數(shù)給r2,用于計算 (a*b)中b值

是不是感覺這段匯編很傻,直接不保存計算不就完了嗎,這個是流程問題,編譯器統(tǒng)一先保存參數(shù),至于你想怎么用它不管,也管不了.

另外返回值都是默認統(tǒng)一給r0保存. square中將(a*b)的結(jié)果給了r0,回到fp中取出R0對fp來說這就是square的返回值,這是規(guī)定.

函數(shù)調(diào)用
main 和 fp 中都需要調(diào)用其他函數(shù),所以都出現(xiàn)了

        push    {r11, lr}
        //....
        pop     {r11, lr}

這哥倆也是成對出現(xiàn)的,這是函數(shù)調(diào)用的必備裝備,作用是保存和恢復(fù)調(diào)用者的現(xiàn)場,例如 main -> fp, fp要保存main的棧幀范圍和指令位置, lr保存的是main函數(shù)執(zhí)行到哪個指令的位置, r11的作用是指向main的棧頂位置,如此fp執(zhí)行完后return回main的時候,先mov pc,lr, PC寄存器的值一變, 表示執(zhí)行的代碼就變了,又回到了main的指令和棧幀繼續(xù)未完成的事業(yè).

內(nèi)存和寄存器數(shù)據(jù)怎么搬?

數(shù)據(jù)主要待在兩個地方:內(nèi)存和寄存器. 寄存器<->寄存器 , 內(nèi)存<->寄存器 , 內(nèi)存<->內(nèi)存 搬運指令都不一樣.

        str     r1, [sp]       @ 寄存器->內(nèi)存
        ldr     r1, [sp, #4]   @ 內(nèi)存->寄存器

這又是一對,用于 內(nèi)存<->寄存器之間,熟知的 mov r0, r1 用于 寄存器<->寄存器

追問三個問題
第一:如果是可變參數(shù)怎么辦? 100個參數(shù)怎么整, 通過寄存器總共就12個,不夠傳參啊

第二:返回值可以有多個嗎?

第三:數(shù)據(jù)搬運可以不經(jīng)過CPU嗎?

寫在最后

  • 如果你覺得這篇內(nèi)容對你還蠻有幫助,我想邀請你幫我三個小忙:
  • 點贊,轉(zhuǎn)發(fā),有你們的 『點贊和評論』,才是我創(chuàng)造的動力。
  • 關(guān)注小編,同時可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識。
  • 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識點,請移步前往小編:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內(nèi)容