標籤:電腦語言 linux 組合語言 mooc
此篇文章出於完成作業的目的,同時也總結一下自己的學習的體會,鞏固一下學習成果。是完全真實的作業過程。如需轉載請保留以下資訊:
陳鐵 + 原創作品轉載請註明出處 + 《Linux核心分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000。
今天電腦已經成為我們生活中重要不可分離的重要組成部分,從隨身攜帶的手機到超級電腦,大部分都遵循馮諾伊曼體繫結構:儲存程式、順序執行。程式編製好後,通過輸入裝置提供給電腦順序執行。只要人可以將需要解決的問題描述為電腦可以順序執行的指令序列,電腦就可以給出相應的結果。所以人們編製了電腦語言用來描述問題,現代電腦語言分為低級語言和進階語言,低級語言更接近機器,進階語言更接近人類。為了描述電腦的工作過程,我們採用接近機器的組合語言(組合語言)描述電腦的執行過程。
實驗環境的主機作業系統是Windows7 64位,運行VirtualBox 4.3.20 Edition,虛擬機器安裝CentOS7.0 64bit,Linux kernel 3.10.0。gcc版本4.8.2,gdb版本GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-51.el7。
以下是作業過程說明:
1.C語言原始碼如下:
#include <stdio.h>
int g(int x) {
return x+2;
}
int f(int x) {
return g(x);
}
int main() {
return f(7)+5;
}
2.執行命令進行gcc -S main.s main.c產生彙編代碼方便我們步進分析代碼的執行情況。執行gcc -g main.c -o main產生可用gdb調試的執行代碼。在linux終端下執行代碼情況如下:
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/5A/36/wKioL1T6j-nBaT5KAAEImRuKXiQ934.jpg" title="byxc.PNG" alt="wKioL1T6j-nBaT5KAAEImRuKXiQ934.jpg" />
3.我們通過分析代碼流程也可以得到正確答案:
程式從main開始執行,調用了f函數,把參數7傳過去賦給x。f函數又調用了g函數,把x也就是7傳過去,g函數得到參數x的值為7,返回7+2=9給f函數,f函數把9返回給main函數,main函數返回9+5=14作為程式的執行結果。在Linux終端下,14儲存在系統變數$?中。
4.電腦系統我們可以抽象簡化為CPU、記憶體、輸入輸出幾部分。下面我們看一下這個程式在我的環境下,電腦是如何機械的的出這個14的。存在函數的程式會大量進行記憶體的堆棧操作,簡單的加法運算在此不展開介紹,重點對於堆棧的操作進行跟蹤。以下是cat main.s 所列出的彙編代碼,僅保留可執行檔部分。
g: pushq %rbp movq %rsp, %rbp movl %edi, -4(%rbp) movl -4(%rbp), %eax addl $2, %eax popq %rbp ret f: pushq %rbp movq %rsp, %rbp subq $8, %rsp movl %edi, -4(%rbp) movl -4(%rbp), %eax movl %eax, %edi call g leave ret main: pushq %rbp movq %rsp, %rbp movl $7, %edi call f addl $5, %eax popq %rbp ret
|
(1)執行gdb main進入調試,l命令可以顯示出C語言代碼。break main設定斷點,使程式直行到程式開始處停下來,然後逐步執行,看一下電腦到底是如何工作的。run命令使程式開始執行。pushq %rbp;movq %rsp, %rbp後,此時rip指向rip=0x40051a。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/5A/3A/wKiom1T6nhXiPrWIAAD7JKsRU-c586.jpg" title="brrun.PNG" alt="wKiom1T6nhXiPrWIAAD7JKsRU-c586.jpg" />
(2)執行 info registers命令查看一下寄存器的情況。堆棧指標rbp和rsp指向相同的地址0x7fffffffe550,表明當前程式堆棧為空白。 650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/5A/36/wKioL1T6n8HzxPK9AAKPTqpeevM709.jpg" title="r1.PNG" alt="wKioL1T6n8HzxPK9AAKPTqpeevM709.jpg" />
這時彙編代碼儲存了堆棧原來的指標,作業系統開始調用main函數。可以看到rip指向下一條要執行指令的地址。
(3)有函數調用,我們在gdb中執行stepi命令。執行movl $7, %edi。
(gdb) stepi 0x000000000040051f 10 return f(7)+5;
(gdb) print $rip $1 = (void (*)()) 0x40051f <main+9> (gdb) print $edi $1 = 7
|
(4)這時把程式中傳給函數f的7儲存進了寄存器edi中,繼續執行,調用f函數。call f,執行的操作是當前rip=0x00400524值壓棧(在gdb中可以執行x %rsp命令查看),rsp-8,f函數所在地址放入rip中。電腦會執行f函數中的pushq $rbp;movq %rsp,%rbp;subq $8, %rsp實際是儲存調用f函數前main函數的指標。此時rbp=0x7fffffffe540,rsp=0x7fffffffe538;而(rbp)儲存著調用前堆棧棧頂地址,當然棧頂移動8個位元組用來接受傳人的參數。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/5A/36/wKioL1T6sJKBVs0QAABm80xKEpw225.jpg" title="r2.PNG" alt="wKioL1T6sJKBVs0QAABm80xKEpw225.jpg" />650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/5A/37/wKioL1T6w8Hxb6qSAABQ4dhCHlI438.jpg" title="r0.PNG" alt="wKioL1T6w8Hxb6qSAABQ4dhCHlI438.jpg" />
(5)執行3次stepi命令,movl %edi, -4(%rbp);movl -4(%rbp), %eax;movl %eax, %edi 這三行指令很明確,從main傳過來的參數存入堆棧空間,然後通過eax寄存器儲存一下,在此放到edi中,準備傳給g函數。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/5A/37/wKioL1T6xMSRqXxEAAE0ROZPtco453.jpg" title="r5.PNG" alt="wKioL1T6xMSRqXxEAAE0ROZPtco453.jpg" />
(6)調用g函數時call g:rip值壓棧;rsp-8;g地址賦給rip。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/5A/3B/wKiom1T6xM_Af9tcAAGql9M-r-A704.jpg" title="r6.PNG" alt="wKiom1T6xM_Af9tcAAGql9M-r-A704.jpg" />
接下來執行兩條初始化指令pushq %rbp;movq %rsp, %rbp,儲存後rbp、rsp變成了0x7fffffffe528。movl %edi, -4(%rbp);movl -4(%rbp), %eax;addl $2, %eax,接受傳入的參數,通過eax執行加法,此時結果儲存在eax中。然後g函數執行恢複處理,popq %rbp;ret,rsp指向0x7fffffffe538。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/5A/36/wKioL1T6t2ygiag8AAEwFi2z_00052.jpg" title="r3.PNG" alt="wKioL1T6t2ygiag8AAEwFi2z_00052.jpg" />
(7)g函數返回時結果儲存在eax中。回到f函數代碼繼續執行。其中leave指令相當於
movq %rbp, %rsp
popq %rbp
程式執行後寄存器情況如下:
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/5A/37/wKioL1T6yeCR8m37AABhvAF94sQ270.jpg" title="r7.PNG" alt="wKioL1T6yeCR8m37AABhvAF94sQ270.jpg" />
ret指令執行後rsp=0x7fffffffe548
(8)完成f函數調用後,回到main函數執行addl $5, %eax語句。結果儲存在eax中。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/5A/36/wKioL1T6uLPwsTMwAAEXKM8g8Cw505.jpg" title="r4.PNG" alt="wKioL1T6uLPwsTMwAAEXKM8g8Cw505.jpg" />
(9)後面代碼是完成main函數返回,原理和一般函數調用相同,在此不在分析。
總結:在我最初接觸電腦的時候,上機的機時還是很奢侈的東西,那時就給自己制定了學習方法:首先根據教材所教的知識,在頭腦中類比電腦會如何執行,假定課本上的例子都是正確的,推匯出機器應當給出什麼樣的結果,當有機會在電腦上操作時再進行驗證。現在看來,那時的思路是對的,但由於沒能堅持,今天的電腦水平還是一般。就本質而言,今天的電腦的確就是類比人的操作過程,程式員如何設計的程式,電腦就會不折不扣的執行。
本文出自 “StudyPark” 部落格,請務必保留此出處http://swordautumn.blog.51cto.com/1485402/1618288
電腦是如何工作的?