CPU 提供了什么

作者: doujiang24
來源:SegmentFault 思否社區(qū)
為了方便理解,CPU 可以簡單認(rèn)為是:
一堆的寄存器,用于暫時存放數(shù)據(jù) 可以執(zhí)行機(jī)器指令,完成運(yùn)算 / 數(shù)據(jù)讀寫 等操作
寄存器
CPU 有很多的寄存器,這里我們只介紹 指令寄存器 和 通用寄存器。
指令寄存器
64 位下,指令寄存器叫 rip (32 位下叫 eip)。
指令寄存器用于存放下一條指令的地址,CPU 的工作模式,就是從 rip 指向的內(nèi)存地址取一條指令,然后執(zhí)行這條指令,同時 rip 指向下一條指令,如此循環(huán),就是 CPU 的基本工作。
也就意味著,通常模式下 CPU 是按照順序執(zhí)行指令的。但是,CPU 也有一些特殊的指令,用于直接修改 rip 的地址。比如,jmp 0xff00 指令,就是把 rip 改為 0xff00,讓 CPU 接下來執(zhí)行內(nèi)存中 0xff00 這個位置的指令。
通用寄存器
以 x86_64 來說,有 16 個“通用”寄存器?!巴ㄓ谩币馕吨梢苑湃我獾臄?shù)據(jù),這 16 個寄存器并沒有什么區(qū)別,但是實際上還是存在一些約定俗稱的用法:
先看看這 8 個:
(這是原來 32 位架構(gòu)下就有的,只是 32 位下是 e 開頭的)
rax: "累加器"(accumulator), 很多加法乘法指令的缺省寄存器,函數(shù)返回值一般也放在這里
rbx: "基地址"(base)寄存器, 在內(nèi)存尋址時存放基地址
rcx: 計數(shù)器(counter), 是重復(fù)(REP)前綴指令和 LOOP 指令的內(nèi)定計數(shù)器
rdx: 用來放整數(shù)除法產(chǎn)生的余數(shù),或者讀寫I/O端口時,用來存放端口號
rsp: 棧頂指針,指向棧的頂部
rbp: 棧底指針,指向棧的底部,通常用`rbp+偏移量`的形式來定位函數(shù)存放在棧中的局部變量
rsi: 字符串操作時,用于存放數(shù)據(jù)源的地址
rdi: 字符串操作時,用于存放目的地址的,和 rsi 經(jīng)常搭配一起使用,執(zhí)行字符串的復(fù)制等操作
另外還有 8 個,是 64 位架構(gòu)下新增的:
r8, r9, r10, r11, r12, r13, r14, r15
機(jī)器指令
在 CPU 的世界里,只有 0 1 這種二進(jìn)制的表示,所以指令也是用 0 1 二進(jìn)制表示的。
然而,二進(jìn)制對人類并不友好,所以有了匯編這種助記符。
算術(shù)運(yùn)算
比如這段加法:
add rax,rdx
比如這個匯編指令,表示:rax = rax + rdx,這就完成了一個加法的運(yùn)算。
通常我們用 rax 寄存器來做加法運(yùn)算,但是其他寄存器一樣也可以完成加法運(yùn)算的,比如:
add rbx,0x1
這個表示 rbx = rbx + 0x1。
這里的加法運(yùn)算,都是在寄存器上完成的,也就是直接修改的寄存器的值。
跳轉(zhuǎn)指令
比如這段無條件跳轉(zhuǎn)指令
jmp 0x269e001c
CPU 默認(rèn)是按照順序執(zhí)行指令的,跳轉(zhuǎn)指令則是,讓 CPU 不再順序執(zhí)行后續(xù)的指令,轉(zhuǎn)而執(zhí)行 0x269e001c 這個內(nèi)存地址中的指令。
具體來說,將指令寄存器中的值改為 0x269e001c 即可,即:rip = 0x269e001c。
內(nèi)存讀寫指令
比如這一對 mov 指令:
mov rbp, [rcx]
mov [rcx], rbp
這里假設(shè) rcx 的值,是一個內(nèi)存地址,比如:0xff00。
第一行 mov 指令,是將內(nèi)存地址 0xff00 中的值,讀取到 rbp 寄存器。
第二行 mov 指令,則是反過來,將 rbp 寄存器的值,寫入到內(nèi)存 0xff00 中。
棧操作
push 和 pop 這一對用于操作“棧”。
“棧”是內(nèi)存空間中的一段地址,我們約定是以棧的形式來使用它,并且用 rsp 寄存器指向棧頂。
棧操作本質(zhì)也是內(nèi)存讀寫操作,只是以棧的方式來使用。
比如這一對:
push rbp
pop rbp
第一行是將 rbp 寄存器中的值壓入棧,等效于:
sub rsp, 8 // rsp = rsp - 8; 棧頂向下生長 8 byte
mov [rsp], rbp // rbp 的值寫入新的棧頂
第二行則是反過來,棧頂彈出一個值,寫入到 rbp 寄存器中,等效于:
mov rbp, [rsp] // 棧頂?shù)闹祵懭?rbp
add rsp, 8 // rsp = rsp + 8; 棧頂向上縮小 8 byte
注意:因為棧在內(nèi)存空間中是倒過來的,所以是向下生長的。

