1217 字
6 分钟
CSAPP_机器级编程

CPU的基本组成:8086/8088架构#

CPU主要由两个主要模块

  1. BIU:总线接口单元,负责与内存和I/O设备进行数据传输

  2. 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时,栈的变化过程如下:

  1. 函数调用前:假设SP指向栈顶位置。

    | | <-低地址
    | |
    | |
    | |
    | |
    | |
    | |
    | |
    | | <- SP, 目前指向栈顶 <- 高地址
  2. 压入参数:在x86架构下,函数参数通常从右到左(或者从后往前)压入栈中。因此,先压入参数b(10),然后压入参数a(5)。

    | | <-低地址
    | |
    | |
    | |
    | |
    | | <- SP, 指向新的栈顶
    | 5| <- 参数a
    | 10| <- 参数b <- 高地址
  3. 调用函数:调用函数时,返回地址会被压入栈中。

    | | <-低地址
    | |
    | |
    | |
    | | <- SP, 指向新的栈顶
    |Ret| <- 返回地址
    | 5| <- 参数a
    | 10| <- 参数b <- 高地址

    23步产生的汇编代码如下:

    push 10 ; 压入参数b
    push 5 ; 压入参数a
    call func ; 调用函数
    add esp, 8 ; 清理栈上的参数(2个参数,每个4字节)
  4. 函数内部:进入函数后将BP设置为当前SP,以便访问参数和局部变量。然后,为局部变量分配空间。

    | | <-低地址
    | |
    | |
    | | <- SP, 指向新的栈顶
    | c | <- 局部变量c
    |ebp| <- ebp=sp
    |Ret| <- 返回地址
    | 5| <- 参数a
    | 10| <- 参数b <- 高地址
    push ebp ; 保存旧的基址指针
    mov ebp, esp ; 设置基址指针
    sub esp, 4 ; 为局部变量c分配空间(4字节)
CSAPP_机器级编程
https://biscuit0613.github.io/posts/csapp/csapp_machinelevelprogramming/
作者
Biscuit
发布于
2025-10-24
许可协议
CC BY-NC-SA 4.0