为了方便理解,CPU 能够简单认为是:架构
CPU 有不少的寄存器,这里咱们只介绍 指令寄存器 和 通用寄存器。函数
64 位下,指令寄存器叫 rip
(32 位下叫 eip
)。
指令寄存器用于存放下一条指令的地址,CPU 的工做模式,就是从 rip
指向的内存地址取一条指令,而后执行这条指令,同时 rip
指向下一条指令,如此循环,就是 CPU 的基本工做。指针
也就意味着,一般模式下 CPU 是按照顺序执行指令的。可是,CPU 也有一些特殊的指令,用于直接修改 rip
的地址。好比,jmp 0xff00
指令,就是把 rip
改成 0xff00
,让 CPU 接下来执行内存中 0xff00
这个位置的指令。code
以 x86_64 来讲,有 16 个“通用”寄存器。“通用”意味着能够听任意的数据,这 16 个寄存器并无什么区别,可是实际上仍是存在一些约定俗称的用法:ip
先看看这 8 个:
(这是原来 32 位架构下就有的,只是 32 位下是 e 开头的)内存
rax: "累加器"(accumulator), 不少加法乘法指令的缺省寄存器,函数返回值通常也放在这里 rbx: "基地址"(base)寄存器, 在内存寻址时存放基地址 rcx: 计数器(counter), 是重复(REP)前缀指令和 LOOP 指令的内定计数器 rdx: 用来放整数除法产生的余数,或者读写I/O端口时,用来存放端口号 rsp: 栈顶指针,指向栈的顶部 rbp: 栈底指针,指向栈的底部,一般用`rbp+偏移量`的形式来定位函数存放在栈中的局部变量 rsi: 字符串操做时,用于存放数据源的地址 rdi: 字符串操做时,用于存放目的地址的,和 rsi 常常搭配一块儿使用,执行字符串的复制等操做
另外还有 8 个,是 64 位架构下新增的:字符串
r8, r9, r10, r11, r12, r13, r14, r15
在 CPU 的世界里,只有 0 1 这种二进制的表示,因此指令也是用 0 1 二进制表示的。
然而,二进制对人类并不友好,因此有了汇编这种助记符。变量
好比这段加法:循环
add rax,rdx
好比这个汇编指令,表示:rax = rax + rdx
,这就完成了一个加法的运算。
一般咱们用 rax
寄存器来作加法运算,可是其余寄存器同样也能够完成加法运算的,好比:二进制
add rbx,0x1
这个表示 rbx = rbx + 0x1
。
这里的加法运算,都是在寄存器上完成的,也就是直接修改的寄存器的值。
好比这段无条件跳转指令
jmp 0x269e001c
CPU 默认是按照顺序执行指令的,跳转指令则是,让 CPU 再也不顺序执行后续的指令,转而执行 0x269e001c
这个内存地址中的指令。
具体来讲,将指令寄存器中的值改成 0x269e001c
便可,即:rip = 0x269e001c
。
好比这一对 mov
指令:
mov rbp, [rcx] mov [rcx], rbp
这里假设 rcx
的值,是一个内存地址,好比:0xff00
。
第一行 mov
指令,是将内存地址 0xff00
中的值,读取到 rbp
寄存器。
第二行 mov
指令,则是反过来,将 rbp
寄存器的值,写入到内存 0xff00
中。
push
和 pop
这一对用于操做“栈”。
“栈”是内存空间中的一段地址,咱们约定是以栈的形式来使用它,而且用 rsp
寄存器指向栈顶。
栈操做本质也是内存读写操做,只是以栈的方式来使用。
好比这一对:
push rbp pop rbp
第一行是将 rbp
寄存器中的值压入栈,等效于:
sub rsp, 8 // rsp = rsp - 8; 栈顶向下生长 8 byte mov [rsp], rbp // rbp 的值写入新的栈顶
第二行则是反过来,栈顶弹出一个值,写入到 rbp
寄存器中,等效于:
mov rbp, [rsp] // 栈顶的值写入 rbp add rsp, 8 // rsp = rsp + 8; 栈顶向上缩小 8 byte
注意:由于栈在内存空间中是倒过来的,因此是向下生长的。