CPU的基本组成:8086/8088架构
CPU主要由两个主要模块
-
BIU:总线接口单元,负责与内存和I/O设备进行数据传输
-
EU:执行单元,负责指令的解码和执行
其中,BIU包含以下组件:
-
指令队列:用于存储从内存中预取的指令,以提高指令获取的效率。
-
地址生成器:负责生成内存地址,用于访问指令和数据。
-
总线控制逻辑:管理CPU与内存和I/O设备之间的数据传输。
-
段寄存器:用于存储段地址,支持分段内存管理。
-
指令指针寄存器:存储下一条要执行的指令的地址。
-
状态寄存器:包含标志位,反映CPU的当前状态。
EU包含以下组件:
- 算术逻辑单元(ALU):执行算术和逻辑运算。
- 通用寄存器:用于存储临时数据和操作数。
- 控制单元:负责指令的解码和执行控制。
- 标志寄存器:存储运算结果的状态标志,如零标志、进位标志等。
8088/8086内存结构
8088/8086架构采用分段内存管理,每个段的大小为64KB。
内存本质上是一个线性地址空间,可以看作一个巨大的数组
每一個元素代表一个字节(8位)
每个字节都有一个唯一的地址,从0开始编号,直到2^20 - 1(即1,048,575),总共可以寻址1MB的内存空间。
内存地址由段地址和偏移地址组成,段地址用于指定段的起始位置,偏移地址用于指定段内的具体位置。
分为以下几种段:
-
代码段(CS):存储指令。
-
数据段(DS):存储全局变量和静态数据。
-
堆栈段(SS):存储函数的调用信息,局部变量等。
-
附加段(ES):用于存储额外的数据,通常用于字符串操作。
栈和堆的区别:
-
栈(Stack):用于存储函数调用时的返回地址、局部变量等数据,遵循后进先出(LIFO)原则。栈的大小通常较小,由操作系统管理。
-
堆(Heap):用于动态分配内存,大小可变,由程序员手动管理。堆中的数据在使用完毕后需要显式释放。
指针寄存器
指针寄存器用于寻址内存栈内的数据。对于8086/8088架构,有以下几种指针寄存器:
-
SP(Stack Pointer):堆栈指针寄存器,指向当前堆栈的顶部位置。堆栈用于存储函数调用时的返回地址、局部变量等数据。
- SP不能用于其他作用
-
BP(Base Pointer):基址指针寄存器,通常用于访问函数的参数和局部变量。BP可以与堆栈段寄存器(SS)结合使用,以便更灵活地访问堆栈中的数据。
- BP可以用于寻址其他内存区域
栈是从高地址向低地址增长,每一次push操作都会使SP减小,每一次pop操作都会使SP增大而
SP和BP增减的值由push/pop的数据类型的大小(例如32位下int占4byte,16位下int占2byte)决定,也由Cpu架构决定,比如16位架构每次push/pop增减2字节,32位架构每次push/pop增减4字节。
这里用32位x86架构举例说明:
有如下c代码
void func(int a, int b) { int c = a + b; // 其他操作}
int main() { func(5, 10); return 0;}main函数调用func时,栈的变化过程如下:
-
函数调用前:假设SP指向栈顶位置。
| | <-低地址| || || || || || || || | <- SP, 目前指向栈顶 <- 高地址 -
压入参数:在x86架构下,函数参数通常从右到左(或者从后往前)压入栈中。因此,先压入参数b(10),然后压入参数a(5)。
| | <-低地址| || || || || | <- SP, 指向新的栈顶| 5| <- 参数a| 10| <- 参数b <- 高地址 -
调用函数:调用函数时,返回地址会被压入栈中。
| | <-低地址| || || || | <- SP, 指向新的栈顶|Ret| <- 返回地址| 5| <- 参数a| 10| <- 参数b <- 高地址23步产生的汇编代码如下:
push 10 ; 压入参数bpush 5 ; 压入参数acall func ; 调用函数add esp, 8 ; 清理栈上的参数(2个参数,每个4字节) -
函数内部:进入函数后将BP设置为当前SP,以便访问参数和局部变量。然后,为局部变量分配空间。
| | <-低地址| || || | <- SP, 指向新的栈顶| c | <- 局部变量c|ebp| <- ebp=sp|Ret| <- 返回地址| 5| <- 参数a| 10| <- 参数b <- 高地址push ebp ; 保存旧的基址指针mov ebp, esp ; 设置基址指针sub esp, 4 ; 为局部变量c分配空间(4字节)