記得在學ARM編程時候學過如何基本的ARM彙編操作,也涉及到一些非常基礎的Linux下的彙編指令形式。但都未真正深入瞭解,也沒寫過多少彙編代碼,除了能夠認識代碼的含義以外,真正上手寫一些彙編代碼進行問題的處理我還在學習中。
談及電腦是如何工作的,似乎在每一本電腦有關的書上都會出現一個名字,那就是馮諾依曼。我記得這也是機房牆上必帖的人物頭像,和圖靈站在一塊。
這篇文章並不過多的論述電腦群組成原理,只是簡單的闡述一下到目前為止我所能記得的領悟的電腦工作的過程。
書上有談到馮諾依曼結構是通用的電腦結構,它也被稱作普林斯頓結構。它只有一個主儲存空間,主儲存空間中可以存放的是資料也可以是指令。並且它只有一種訪問主儲存空間的指令。CPU通過匯流排與儲存空間進行資訊交換。這種結構事實上是不高效的,因為資料和指令都雜合在一起。所以,對應的比較好的設計就是哈佛結構,哈佛結構是指令與資料分開儲存的體繫結構。它有存放指令和資料的分離的地址空間和訪問指令,所以這裡就有兩類匯流排,資料匯流排和指令匯流排。因為進行了職責分離,因此哈佛結構的電腦處理器具有更高的資料吞吐率。
電腦工作的原理其實就在於對資料的處理,而對資料的處理通過調用指令的方式進行。我們平時操作電腦,可以通過鍵盤,通過滑鼠操作,對應的是電腦電路0,1的電位變化。而如何解釋操作則是由作業系統來進行處理,總而言之,電腦工作過程都被進行了抽象與封裝,而作業系統是對底層的第一層封裝。最基礎的電腦由儲存空間,輸入輸出裝置,螢幕,主板,CPU等硬體組成。如何驅動這些硬體則是由軟體來進行。
編程是在作業系統的基礎上進行的通過代碼的方式進行操作再由編譯器進行編譯來執行。
在Linux下我們進行了反組譯碼來測試了一下C代碼如何轉換成彙編代碼的過程。
我們在實驗樓這個平台進行,C代碼是簡單的三個函數(一個主函數和兩個被調用函數):
在Linux下用GCC進行反組譯碼:
gcc -S -o main.s main.c -m32
則可以得到main.c的彙編級代碼:
以點開頭的是注釋類所以刪去了大部分的這樣的語句,以上截圖是為全部的彙編代碼。
首先從main:開始看起:
pushl %ebp
ebp是基礎棧指標,假想為0,此時棧為空白,esp和ebp都指向棧底,這條指令等價於:
subl $4, %esp
movl %ebp,(%esp)
即:將esp指向的位置向下減去4,再把esp對應處的值設為ebp的位置,假設為0;
movl %esp, %ebp
是將ebp指標值改為esp指標的值,即esp再次和ebp指向同一個位置;
subl $4, %esp
movl $8, (%esp)
這兩條指令等價於pushl $8, esp指標向下移動4個位元組;再把esp指向的寄存器處值設為8;
call f
調用函數f,這條指令等價於
pushl %eip(*)
movl f %eip(*)
意思是先暫存eip的值,eip是指向要調用指令的位置,在圖中是第24行,存在寄存器中,然後對eip進行賦值為f的入口地址,執行f函數的代碼;
pushl %ebp
esp向下走4個位元組,並把ebp指標值壓棧
movl %esp, %ebp
將ebp的值設定為何esp相同
subl $4,%esp
esp向下走4個位元組
movl 8(%ebp), %eax
eax寄存器的值等於ebp+8,即eax現在指向數字8
movl %eax, (%esp)
將esp位置的值設為eax指標值
call g
等價於
pushl %eip(*)
movl g %eip(*)
調用函數g, 將eip的值存在寄存器,圖中是16行,再對eip進行賦值,進入g函數入口;
pushl %ebp
esp向下進4個位元組並把ebp指標值放進來
movl %esp, %ebp
使得ebp=esp
movl 8(%ebp), %eax
令eax=ebp+8,向上走8個位元組指向儲存著eax值,這個eax指向的是數字8
addl $10,%eax
把eax指向的值與10相加得到18
popl %ebp
等價於movl (%esp), %ebp
addl $4, %esp
然後:
ret
等價於 popl %eip(*)
跳轉到16行進行執行
leave
等價於
movl %ebp, %esp
popl %ebp
再彈出eip跳轉到第24行暫存的eax值為18,再與3相加,再leave,再ret,ebp與esp均回到初值。
王兵原創作品轉載請註明出處 《Linux核心分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000