在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

CPU如何執(zhí)行代碼 匯編語言教程

奈因PCB電路板設(shè)計 ? 來源:博客 ? 作者:阮一峰的網(wǎng)絡(luò)日志 ? 2021-08-25 11:14 ? 次閱讀

學(xué)習(xí)編程其實(shí)就是學(xué)高級語言,即那些為人類設(shè)計的計算機(jī)語言。

但是,計算機(jī)不理解高級語言,必須通過編譯器轉(zhuǎn)成二進(jìn)制代碼,才能運(yùn)行。學(xué)會高級語言,并不等于理解計算機(jī)實(shí)際的運(yùn)行步驟。

計算機(jī)真正能夠理解的是低級語言,它專門用來控制硬件。匯編語言就是低級語言,直接描述/控制 CPU 的運(yùn)行。如果你想了解 CPU 到底干了些什么,以及代碼的運(yùn)行步驟,就一定要學(xué)習(xí)匯編語言。

匯編語言不容易學(xué)習(xí),就連簡明扼要的介紹都很難找到。下面我嘗試寫一篇最好懂的匯編語言教程,解釋 CPU 如何執(zhí)行代碼。

一、匯編語言是什么?

我們知道,CPU 只負(fù)責(zé)計算,本身不具備智能。你輸入一條指令(instruction),它就運(yùn)行一次,然后停下來,等待下一條指令。

這些指令都是二進(jìn)制的,稱為操作碼(opcode),比如加法指令就是00000011。編譯器的作用,就是將高級語言寫好的程序,翻譯成一條條操作碼。

對于人類來說,二進(jìn)制程序是不可讀的,根本看不出來機(jī)器干了什么。為了解決可讀性的問題,以及偶爾的編輯需求,就誕生了匯編語言。

匯編語言是二進(jìn)制指令的文本形式,與指令是一一對應(yīng)的關(guān)系。比如,加法指令00000011寫成匯編語言就是 ADD。只要還原成二進(jìn)制,匯編語言就可以被 CPU 直接執(zhí)行,所以它是最底層的低級語言。

二、來歷

最早的時候,編寫程序就是手寫二進(jìn)制指令,然后通過各種開關(guān)輸入計算機(jī),比如要做加法了,就按一下加法開關(guān)。后來,發(fā)明了紙帶打孔機(jī),通過在紙帶上打孔,將二進(jìn)制指令自動輸入計算機(jī)。

為了解決二進(jìn)制指令的可讀性問題,工程師將那些指令寫成了八進(jìn)制。二進(jìn)制轉(zhuǎn)八進(jìn)制是輕而易舉的,但是八進(jìn)制的可讀性也不行。很自然地,最后還是用文字表達(dá),加法指令寫成 ADD。內(nèi)存地址也不再直接引用,而是用標(biāo)簽表示。

這樣的話,就多出一個步驟,要把這些文字指令翻譯成二進(jìn)制,這個步驟就稱為 assembling,完成這個步驟的程序就叫做 assembler。它處理的文本,自然就叫做 aseembly code。標(biāo)準(zhǔn)化以后,稱為 assembly language,縮寫為 asm,中文譯為匯編語言。

每一種 CPU 的機(jī)器指令都是不一樣的,因此對應(yīng)的匯編語言也不一樣。本文介紹的是目前最常見的 x86 匯編語言,即 Intel 公司的 CPU 使用的那一種。

三、寄存器

學(xué)習(xí)匯編語言,首先必須了解兩個知識點(diǎn):寄存器和內(nèi)存模型。

先來看寄存器。CPU 本身只負(fù)責(zé)運(yùn)算,不負(fù)責(zé)儲存數(shù)據(jù)。數(shù)據(jù)一般都儲存在內(nèi)存之中,CPU 要用的時候就去內(nèi)存讀寫數(shù)據(jù)。但是,CPU 的運(yùn)算速度遠(yuǎn)高于內(nèi)存的讀寫速度,為了避免被拖慢,CPU 都自帶一級緩存和二級緩存。基本上,CPU 緩存可以看作是讀寫速度較快的內(nèi)存。

但是,CPU 緩存還是不夠快,另外數(shù)據(jù)在緩存里面的地址是不固定的,CPU 每次讀寫都要尋址也會拖慢速度。因此,除了緩存之外,CPU 還自帶了寄存器(register),用來儲存最常用的數(shù)據(jù)。也就是說,那些最頻繁讀寫的數(shù)據(jù)(比如循環(huán)變量),都會放在寄存器里面,CPU 優(yōu)先讀寫寄存器,再由寄存器跟內(nèi)存交換數(shù)據(jù)。

寄存器不依靠地址區(qū)分?jǐn)?shù)據(jù),而依靠名稱。每一個寄存器都有自己的名稱,我們告訴 CPU 去具體的哪一個寄存器拿數(shù)據(jù),這樣的速度是最快的。有人比喻寄存器是 CPU 的零級緩存。

四、寄存器的種類

早期的 x86 CPU 只有8個寄存器,而且每個都有不同的用途。現(xiàn)在的寄存器已經(jīng)有100多個了,都變成通用寄存器,不特別指定用途了,但是早期寄存器的名字都被保存了下來。

EAX

EBX

ECX

EDX

EDI

ESI

EBP

ESP

上面這8個寄存器之中,前面七個都是通用的。ESP 寄存器有特定用途,保存當(dāng)前 Stack 的地址(詳見下一節(jié))。我們常常看到 32位 CPU、64位 CPU 這樣的名稱,其實(shí)指的就是寄存器的大小。32 位 CPU 的寄存器大小就是4個字節(jié)。

五、內(nèi)存模型:Heap

寄存器只能存放很少量的數(shù)據(jù),大多數(shù)時候,CPU 要指揮寄存器,直接跟內(nèi)存交換數(shù)據(jù)。所以,除了寄存器,還必須了解內(nèi)存怎么儲存數(shù)據(jù)。

程序運(yùn)行的時候,操作系統(tǒng)會給它分配一段內(nèi)存,用來儲存程序和運(yùn)行產(chǎn)生的數(shù)據(jù)。這段內(nèi)存有起始地址和結(jié)束地址,比如從0x1000到0x8000,起始地址是較小的那個地址,結(jié)束地址是較大的那個地址。

程序運(yùn)行過程中,對于動態(tài)的內(nèi)存占用請求(比如新建對象,或者使用malloc命令),系統(tǒng)就會從預(yù)先分配好的那段內(nèi)存之中,劃出一部分給用戶,具體規(guī)則是從起始地址開始劃分(實(shí)際上,起始地址會有一段靜態(tài)數(shù)據(jù),這里忽略)。舉例來說,用戶要求得到10個字節(jié)內(nèi)存,那么從起始地址0x1000開始給他分配,一直分配到地址0x100A,如果再要求得到22個字節(jié),那么就分配到0x1020。

這種因?yàn)橛脩糁鲃诱埱蠖鴦澐殖鰜淼膬?nèi)存區(qū)域,叫做 Heap(堆)。它由起始地址開始,從低位(地址)向高位(地址)增長。Heap 的一個重要特點(diǎn)就是不會自動消失,必須手動釋放,或者由垃圾回收機(jī)制來回收。

六、內(nèi)存模型:Stack

除了 Heap 以外,其他的內(nèi)存占用叫做 Stack(棧)。簡單說,Stack 是由于函數(shù)運(yùn)行而臨時占用的內(nèi)存區(qū)域。

請看下面的例子。

intmain(){ inta=2; intb=3; }

上面代碼中,系統(tǒng)開始執(zhí)行main函數(shù)時,會為它在內(nèi)存里面建立一個幀(frame),所有main的內(nèi)部變量(比如a和b)都保存在這個幀里面。main函數(shù)執(zhí)行結(jié)束后,該幀就會被回收,釋放所有的內(nèi)部變量,不再占用空間。

如果函數(shù)內(nèi)部調(diào)用了其他函數(shù),會發(fā)生什么情況?

intmain(){ inta=2; intb=3; returnadd_a_and_b(a,b); }

上面代碼中,main函數(shù)內(nèi)部調(diào)用了add_a_and_b函數(shù)。執(zhí)行到這一行的時候,系統(tǒng)也會為add_a_and_b新建一個幀,用來儲存它的內(nèi)部變量。也就是說,此時同時存在兩個幀:main和add_a_and_b。一般來說,調(diào)用棧有多少層,就有多少幀。

等到add_a_and_b運(yùn)行結(jié)束,它的幀就會被回收,系統(tǒng)會回到函數(shù)main剛才中斷執(zhí)行的地方,繼續(xù)往下執(zhí)行。通過這種機(jī)制,就實(shí)現(xiàn)了函數(shù)的層層調(diào)用,并且每一層都能使用自己的本地變量。

所有的幀都存放在 Stack,由于幀是一層層疊加的,所以 Stack 叫做棧。生成新的幀,叫做"入棧",英文是 push;棧的回收叫做"出棧",英文是 pop。Stack 的特點(diǎn)就是,最晚入棧的幀最早出棧(因?yàn)樽顑?nèi)層的函數(shù)調(diào)用,最先結(jié)束運(yùn)行),這就叫做"后進(jìn)先出"的數(shù)據(jù)結(jié)構(gòu)。每一次函數(shù)執(zhí)行結(jié)束,就自動釋放一個幀,所有函數(shù)執(zhí)行結(jié)束,整個 Stack 就都釋放了。

Stack 是由內(nèi)存區(qū)域的結(jié)束地址開始,從高位(地址)向低位(地址)分配。比如,內(nèi)存區(qū)域的結(jié)束地址是0x8000,第一幀假定是16字節(jié),那么下一次分配的地址就會從0x7FF0開始;第二幀假定需要64字節(jié),那么地址就會移動到0x7FB0。

七、CPU 指令

7.1 一個實(shí)例

了解寄存器和內(nèi)存模型以后,就可以來看匯編語言到底是什么了。下面是一個簡單的程序example.c。

intadd_a_and_b(inta,intb){ returna+b; } intmain(){ returnadd_a_and_b(2,3); }

gcc 將這個程序轉(zhuǎn)成匯編語言。

$ gcc -S example.c

上面的命令執(zhí)行以后,會生成一個文本文件example.s,里面就是匯編語言,包含了幾十行指令。這么說吧,一個高級語言的簡單操作,底層可能由幾個,甚至幾十個 CPU 指令構(gòu)成。CPU 依次執(zhí)行這些指令,完成這一步操作。

example.s經(jīng)過簡化以后,大概是下面的樣子。

_add_a_and_b: push %ebx mov %eax, [%esp+8] mov %ebx, [%esp+12] add %eax, %ebx pop %ebx ret _main: push 3 push 2 call _add_a_and_b add %esp, 8 ret

可以看到,原程序的兩個函數(shù)add_a_and_b和main,對應(yīng)兩個標(biāo)簽_add_a_and_b和_main。每個標(biāo)簽里面是該函數(shù)所轉(zhuǎn)成的 CPU 運(yùn)行流程。

每一行就是 CPU 執(zhí)行的一次操作。它又分成兩部分,就以其中一行為例。

push %ebx

這一行里面,push是 CPU 指令,%ebx是該指令要用到的運(yùn)算子。一個 CPU 指令可以有零個到多個運(yùn)算子。

下面我就一行一行講解這個匯編程序,建議讀者最好把這個程序,在另一個窗口拷貝一份,省得閱讀的時候再把頁面滾動上來。

7.2 push 指令

根據(jù)約定,程序從_main標(biāo)簽開始執(zhí)行,這時會在 Stack 上為main建立一個幀,并將 Stack 所指向的地址,寫入 ESP 寄存器。后面如果有數(shù)據(jù)要寫入main這個幀,就會寫在 ESP 寄存器所保存的地址。

然后,開始執(zhí)行第一行代碼。

push 3

push指令用于將運(yùn)算子放入 Stack,這里就是將3寫入main這個幀。

雖然看上去很簡單,push指令其實(shí)有一個前置操作。它會先取出 ESP 寄存器里面的地址,將其減去4個字節(jié),然后將新地址寫入 ESP 寄存器。使用減法是因?yàn)?Stack 從高位向低位發(fā)展,4個字節(jié)則是因?yàn)?的類型是int,占用4個字節(jié)。得到新地址以后, 3 就會寫入這個地址開始的四個字節(jié)。

push 2

第二行也是一樣,push指令將2寫入main這個幀,位置緊貼著前面寫入的3。這時,ESP 寄存器會再減去 4個字節(jié)(累計減去8)。

1d84b766-e06d-11eb-9e57-12bb97331649.png

7.3 call 指令

第三行的call指令用來調(diào)用函數(shù)。

call _add_a_and_b

上面的代碼表示調(diào)用add_a_and_b函數(shù)。這時,程序就會去找_add_a_and_b標(biāo)簽,并為該函數(shù)建立一個新的幀。

下面就開始執(zhí)行_add_a_and_b的代碼。

push %ebx

這一行表示將 EBX 寄存器里面的值,寫入_add_a_and_b這個幀。這是因?yàn)楹竺嬉玫竭@個寄存器,就先把里面的值取出來,用完后再寫回去。

這時,push指令會再將 ESP 寄存器里面的地址減去4個字節(jié)(累計減去12)。

7.4 mov 指令

mov指令用于將一個值寫入某個寄存器。

mov %eax, [%esp+8]

這一行代碼表示,先將 ESP 寄存器里面的地址加上8個字節(jié),得到一個新的地址,然后按照這個地址在 Stack 取出數(shù)據(jù)。根據(jù)前面的步驟,可以推算出這里取出的是2,再將2寫入 EAX 寄存器。

下一行代碼也是干同樣的事情。

mov %ebx, [%esp+12]

上面的代碼將 ESP 寄存器的值加12個字節(jié),再按照這個地址在 Stack 取出數(shù)據(jù),這次取出的是3,將其寫入 EBX 寄存器。

7.5 add 指令

add指令用于將兩個運(yùn)算子相加,并將結(jié)果寫入第一個運(yùn)算子。

add %eax, %ebx

上面的代碼將 EAX 寄存器的值(即2)加上 EBX 寄存器的值(即3),得到結(jié)果5,再將這個結(jié)果寫入第一個運(yùn)算子 EAX 寄存器。

7.6 pop 指令

pop指令用于取出 Stack 最近一個寫入的值(即最低位地址的值),并將這個值寫入運(yùn)算子指定的位置。

pop %ebx

上面的代碼表示,取出 Stack 最近寫入的值(即 EBX 寄存器的原始值),再將這個值寫回 EBX 寄存器(因?yàn)榧臃ㄒ呀?jīng)做完了,EBX 寄存器用不到了)。

注意,pop指令還會將 ESP 寄存器里面的地址加4,即回收4個字節(jié)。

7.7 ret 指令

ret指令用于終止當(dāng)前函數(shù)的執(zhí)行,將運(yùn)行權(quán)交還給上層函數(shù)。也就是,當(dāng)前函數(shù)的幀將被回收。

ret

可以看到,該指令沒有運(yùn)算子。

隨著add_a_and_b函數(shù)終止執(zhí)行,系統(tǒng)就回到剛才main函數(shù)中斷的地方,繼續(xù)往下執(zhí)行。

add %esp, 8

上面的代碼表示,將 ESP 寄存器里面的地址,手動加上8個字節(jié),再寫回 ESP 寄存器。這是因?yàn)?ESP 寄存器的是 Stack 的寫入開始地址,前面的pop操作已經(jīng)回收了4個字節(jié),這里再回收8個字節(jié),等于全部回收。

ret

最后,main函數(shù)運(yùn)行結(jié)束,ret指令退出程序執(zhí)行。

看完這篇文章,你對匯編語言有什么了解了嗎,看嘛,匯編也不難~

原文鏈接:

http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 計算機(jī)
    +關(guān)注

    關(guān)注

    19

    文章

    7633

    瀏覽量

    90238
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3683

    瀏覽量

    94899
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4889

    瀏覽量

    70287

原文標(biāo)題:計算機(jī)怎么識別你寫的代碼?

文章出處:【微信號:pcbgood,微信公眾號:奈因PCB電路板設(shè)計】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    飛凌嵌入式ElfBoard ELF 1板卡-uboot啟動流程分析之uboot啟動階段

    uboot啟動可分為匯編語言執(zhí)行和C語言執(zhí)行兩個階段,兩個階段以_main函數(shù)為分界。uboot第一階段由_start (arch/arm/lib/vectors.S)進(jìn)入,然后跳轉(zhuǎn)到
    發(fā)表于 05-30 09:24

    CPU的各種指令和執(zhí)行流程

    在集成電路設(shè)計中,CPU的指令是指計算機(jī)中央處理單元(CPU)用來執(zhí)行計算任務(wù)的基本操作指令集。這些指令是CPU能夠理解并執(zhí)行的二進(jìn)制
    的頭像 發(fā)表于 04-18 11:24 ?610次閱讀

    TMS320C6000匯編語言工具v7.4用戶指南

    電子發(fā)燒友網(wǎng)站提供《TMS320C6000匯編語言工具v7.4用戶指南.pdf》資料免費(fèi)下載
    發(fā)表于 12-30 16:42 ?0次下載
    TMS320C6000<b class='flag-5'>匯編語言</b>工具v7.4用戶指南

    TMS320C6000匯編語言工具 v8.3.x

    電子發(fā)燒友網(wǎng)站提供《TMS320C6000匯編語言工具 v8.3.x.pdf》資料免費(fèi)下載
    發(fā)表于 11-07 10:00 ?0次下載
    TMS320C6000<b class='flag-5'>匯編語言</b>工具 v8.3.x

    ARM匯編語言工具

    電子發(fā)燒友網(wǎng)站提供《ARM匯編語言工具.pdf》資料免費(fèi)下載
    發(fā)表于 11-06 09:12 ?2次下載
    ARM<b class='flag-5'>匯編語言</b>工具

    TMS320C28x匯編語言工具

    電子發(fā)燒友網(wǎng)站提供《TMS320C28x匯編語言工具.pdf》資料免費(fèi)下載
    發(fā)表于 11-01 09:32 ?2次下載
    TMS320C28x<b class='flag-5'>匯編語言</b>工具

    C語言生成可執(zhí)行二進(jìn)制文件的具體過程

    C語言源碼到生成可執(zhí)行文件的過程通常包括預(yù)處理(Preprocessing)、編譯(Compilation)、匯編(Assembly)、鏈接(Linking)等多個步驟,每個步驟都有其特定的任務(wù)
    的頭像 發(fā)表于 10-21 14:30 ?1459次閱讀
    C<b class='flag-5'>語言</b>生成可<b class='flag-5'>執(zhí)行</b>二進(jìn)制文件的具體過程

    TMS320LF240x DSP的C語言匯編代碼快速入門

    電子發(fā)燒友網(wǎng)站提供《TMS320LF240x DSP的C語言匯編代碼快速入門.pdf》資料免費(fèi)下載
    發(fā)表于 10-18 10:14 ?1次下載
    TMS320LF240x DSP的C<b class='flag-5'>語言</b>和<b class='flag-5'>匯編</b><b class='flag-5'>代碼</b>快速入門

    hex文件怎么能轉(zhuǎn)回去c語言

    將 .hex 文件直接“轉(zhuǎn)回去”為原始的C語言代碼是不可能的,因?yàn)?.hex 文件是編譯后的二進(jìn)制文件,它包含了機(jī)器碼,這些機(jī)器碼是處理器可以直接執(zhí)行的指令,與原始的C語言
    的頭像 發(fā)表于 09-02 10:46 ?4091次閱讀

    hex可以轉(zhuǎn)成源代碼

    ,可以通過以下幾種方法嘗試獲取源代碼的近似形式: 反匯編 : 使用反匯編工具可以將Hex文件中的機(jī)器碼轉(zhuǎn)換回匯編語言匯編語言是一種低級
    的頭像 發(fā)表于 09-02 10:41 ?2070次閱讀

    hex文件如何查看原c語言代碼

    是處理器可以直接執(zhí)行的指令,而 C 語言代碼則是人類可讀的高級編程語言代碼。 然而,如果你想要從 .hex 文件中獲取一些有用的信息或者對程
    的頭像 發(fā)表于 09-02 10:37 ?4463次閱讀

    RISC-V匯編語言

    一、導(dǎo)言 從 C 程序翻譯成為可以在計算機(jī)上執(zhí)行的機(jī)器語言程序的四個經(jīng)典步驟。這一章的內(nèi)容包括了后三個步驟,不過我們要從匯編語言在 RISC-V 函數(shù)調(diào)用規(guī)范中的作用開始說起。 上圖從C源代碼
    發(fā)表于 08-19 18:07

    RISC-V 匯編語言程序的調(diào)試

    編譯,不做動態(tài)的特殊處理 -g 加入調(diào)試信息(符號表等),便于調(diào)試 -o 輸出文件 -Ttext 0x80000000把代碼段放到這個位置(模擬器剛剛啟動的時候,會從0x1000的位置執(zhí)行,這個
    發(fā)表于 07-19 16:27

    RISC-V 匯編在線仿真器 Ripes OnLine 使用說明

    沒有專用的 IO 寄存器,并且在從0xf0000000開始的一系列內(nèi)存地址上與此模塊通信。 編輯器面板 進(jìn)入編輯器面板,在源代碼面板中輸入程序,網(wǎng)頁可以運(yùn)行匯編語言C/C++的程序,需要你添加編譯器
    發(fā)表于 06-28 14:59
    主站蜘蛛池模板: 视频在线免费观看 | 午夜一级黄色片 | 日本黄色免费网址 | 全黄性色大片 | 午夜看黄网站免费 | sss欧美华人整片在线观看 | 黄色小视频日本 | 末发育娇小性色xxxxx视频 | 一级片成人 | 免费大片av手机看片 | 日本黄色大片在线播放视频免费观看 | 天天天天天干 | 色激情小说 | 亚洲欧美日韩另类精品一区二区三区 | 亚洲亚洲人成网站在线观看 | 一级黄色片a | 4438x成人网最大色成网站 | 天天射天天操天天色 | 日本特黄特色aaa大片免费欧 | 免费在线观看a | qvod高清在线成人观看 | 天天做日日干 | 94在线 | 久久精品re | 亚洲91精品 | 永久免费品色堂 | 成人做视频免费 | 午夜影视啪啪免费体验区入口 | 午夜性视频播放免费视频 | 亚洲日本中文字幕天天更新 | 国产精品资源手机在线播放 | 欧美作爱福利免费观看视频 | 国产三级国产精品 | 无限国产资源 | 日日噜噜夜夜狠狠va视频 | 久青草免费视频手机在线观看 | 久久大伊人 | 免费的很黄很色的床小视频 | 免费国产午夜在线观看 | 特黄特色的大片观看免费视频 | 好大好猛好爽好深视频免费 |