安大大?+ 原創(chuàng)作品轉(zhuǎn)載請(qǐng)注明出處 + 《Linux操作系統(tǒng)分析》MOOC課程
計(jì)算機(jī)是如何工作的:
cpu通過(guò)總線與內(nèi)存相連该押。cpu中有一個(gè)非常重要的寄存器IP(Instruction Pointer)穴店,IP指向內(nèi)存當(dāng)中的代碼段关噪。cpu不斷的從IP指向的內(nèi)存地址取來(lái)一條指令執(zhí)行,然后自加1蜕提,執(zhí)行下一條指令森书。計(jì)算機(jī)就是如此反復(fù)不斷的執(zhí)行下一條指令而工作的。
實(shí)驗(yàn):
首先在目錄下新建一個(gè)文件?
$ vi main.c
把如下的代碼粘貼進(jìn)去:
使用?gcc -S -o main.s main.c -m32 命令谎势,將main.c編譯成匯編代碼凛膏。-m32是把main.c編譯成一個(gè)32位的匯編代碼。
命令執(zhí)行結(jié)束后脏榆,該目錄下會(huì)生成一個(gè)main.s文件猖毫,即為得到的匯編代碼。".s"是匯編代碼的擴(kuò)展名须喂。使用 $ vi main.s 命令打開(kāi)這個(gè)文件:
其中以點(diǎn)開(kāi)頭的是用于鏈接時(shí)的輔助信息吁断,不會(huì)在實(shí)際中執(zhí)行,刪除這些以點(diǎn)開(kāi)頭的內(nèi)容坞生,留下來(lái)純匯編的代碼仔役。
得到的匯編代碼,其中包括三個(gè)函數(shù)是己,main,f,g又兵,與c語(yǔ)言代碼分別對(duì)應(yīng)。
ebp指向堆棧的棧底卒废,esp指向堆棧的棧頂寒波。函數(shù)調(diào)用堆棧是由邏輯上多個(gè)堆棧疊加起來(lái)的,棧底是相對(duì)的棧底升熊。當(dāng)前函數(shù)有它自己的堆棧俄烁,和相對(duì)的棧底。跳出當(dāng)前函數(shù)之后另一個(gè)函數(shù)還有自己的堆棧和棧底级野。
eax用于暫存一些數(shù)值页屠。函數(shù)的返回值默認(rèn)使用eax寄存器存儲(chǔ)返回給上一級(jí)函數(shù)粹胯。
執(zhí)行過(guò)程:
為了方便,把地址設(shè)為標(biāo)號(hào)辰企,初地址為標(biāo)號(hào)0风纠,壓一次棧為1,兩個(gè)標(biāo)號(hào)之間相差4個(gè)字節(jié)牢贸,低標(biāo)號(hào)是高地址竹观,高的標(biāo)號(hào)是低地址。起初ebp和esp都設(shè)為0(相對(duì)的)潜索。程序是從main函數(shù)開(kāi)始的臭增,所以剛開(kāi)始eip指向main標(biāo)號(hào)的位置,行號(hào)17竹习。
標(biāo)號(hào)的第一條指令就是18 ?pushl %ebp
下一條指令:19 ?movl %esp, %ebp:esp賦給ebp:
20 ?subl $4, %esp:$開(kāi)頭為立即數(shù)誊抛,esp向下減4,即向下移動(dòng)一個(gè)標(biāo)號(hào):
21 ?movl $7, (%esp) :把立即數(shù)7放到%esp指向的位置(標(biāo)號(hào)2的位置):
22? call f:call f 相當(dāng)于pushl %eip整陌,然后movl f %eip拗窃。當(dāng)執(zhí)行call這個(gè)動(dòng)作的時(shí)候,實(shí)際上eip指向的是call的下一條指令23行泌辫。所以push進(jìn)棧的是23随夸。esp指向3,eip跳轉(zhuǎn)到f:標(biāo)號(hào)的位置第8行震放。
9 ?pushl %ebp:此時(shí)ebp指向標(biāo)號(hào)1逃魄,所以把1壓棧
10? movl %esp, %ebp:
11 ?subl $4, %esp:
12 ?movl 8(%ebp), %eax:ebp變址尋址加8,向上加兩個(gè)標(biāo)號(hào)的位置澜搅,它的內(nèi)容是7伍俘,eax=7:
13 ?movl %eax, (%esp):把eax放到esp的位置:
14? call g:相當(dāng)于pushl %eip,然后movl g %eip勉躺。eip指向的是call的下一條指令15行癌瘾,所以壓入15。eip跳轉(zhuǎn)到g:標(biāo)號(hào)的位置第1行饵溅。
2? pushl %ebp:
3 ?movl %esp, %ebp:
4? movl 8(%ebp), %eax:ebp變址尋址加8妨退,向上加兩個(gè)標(biāo)號(hào)的位置,它的內(nèi)容是7蜕企,eax=7
5? addl $4, %eax:把立即數(shù)4加到eax咬荷,eax=7+4=11,eax=11
6 ?popl %ebp:把esp里的內(nèi)容放到ebp里轻掩,然后esp向上移動(dòng)4幸乒,即1個(gè)標(biāo)號(hào)。效果是ebp又指向了原來(lái)標(biāo)號(hào)4的位置:
7? ret:ret就是popl %eip唇牧,執(zhí)行之后eip指向了第15行罕扎,即call g的下一行:
15 ?leave:leave是先movl %ebp,%esp然后popl %ebp聚唐,記得pop之后esp上移一個(gè)標(biāo)號(hào):
16? ret:即popl %eip,esp向上移動(dòng)一位腔召,同時(shí)eip指向了23杆查,程序跳轉(zhuǎn)到執(zhí)行第23行,即call f的下一條指令:
23 ?addl $2, %eax:eax=11+2=13臀蛛,eax存儲(chǔ)了默認(rèn)的返回值亲桦。
24? leave:先movl %ebp,%esp再popl %ebp:
這個(gè)時(shí)候,椬瞧停回到了main函數(shù)最初的狀態(tài)客峭。
25 ?ret:return到了main函數(shù)之前的堆棧了,可能再main函數(shù)之前還有一個(gè)eip氧卧,這些由操作系統(tǒng)管理桃笙。
整個(gè)過(guò)程堆棧先是向下增長(zhǎng)氏堤,然后向上還原沙绝,堆棧增增減減,把程序變成了指令流鼠锈,從cpu上流了一遍闪檬。
總結(jié)
計(jì)算機(jī)的工作就是cpu就是不斷的通過(guò)ip從內(nèi)存當(dāng)中取出指令,解釋并執(zhí)行的购笆。C語(yǔ)言中的if else粗悯、函數(shù)調(diào)用、return等同欠,對(duì)應(yīng)著匯編當(dāng)中的conditional JMP样傍、CALL、RET等铺遂。通過(guò)這些指令來(lái)控制程序的流程衫哥,ip指針的跳轉(zhuǎn)。函數(shù)調(diào)用時(shí)襟锐,會(huì)把先前的堆棧(包括信息撤逢、狀態(tài))保存下來(lái),然后再在其上疊加一個(gè)新的堆棧粮坞。當(dāng)被調(diào)用的函數(shù)執(zhí)行完后蚊荣,這個(gè)新的堆棧將會(huì)彈出,返回到上一層的函數(shù)堆棧莫杈,還可以得到之前堆棧當(dāng)中保存的信息互例、狀態(tài)。在這個(gè)過(guò)程當(dāng)中筝闹,eip敲霍、ebp俊马、esp、eax等寄存器不斷的變化肩杈,堆棧不斷的增減柴我,整個(gè)程序變成指令流,從cpu上流過(guò)扩然。這是我對(duì)計(jì)算機(jī)如何工作的理解艘儒。