CSAPP - 03.Machine-Level Programming I

对干机器级编程来说,其中两种抽象尤为重要。第一种是由指令集体系结构或指令集架构(Instruction Set Architecture,ISA)来定义机器级程序的格式和行为,它定义了处理器状态、指令的格式,以及每条指令对状态的影响。

机器级编程 I: (Machine-Level Programming I):

主要内容
本章是机器级编程的入门,重点介绍 汇编语言的基础。包括:

  1. C 代码如何变成机器码:编译、汇编、链接的流程。
  2. 硬件的“程序员视角”:介绍了 CPU 中你可以直接操作的资源,特别是 16个通用寄存器%rax, %rbx 等)。
  3. 基本指令:如何移动数据(movq)、如何访问内存(寻址模式)、以及基本的算术运算(addq, leaq)。

Motivation
计算机不认识 C 语言,只认识二进制指令。要理解程序的性能瓶颈调试底层 Bug 或者理解指针的本质,你必须掌握 CPU 是如何通过寄存器和内存搬运数据的。这是理解计算机系统的基石。

1.1 Intel处理器与体系结构简史

  • CISC vs. RISC:
    • Intel x86 属于 复杂指令集计算机 (CISC)。其特点是指令集庞大,指令功能复杂多样,格式不一。
    • 与之相对的是 精简指令集计算机 (RISC),例如 ARM 和 RISC-V。RISC的指令数量少,格式统一,易于优化。近年来,RISC在低功耗和移动设备领域复兴。
  • x86 发展里程碑:
    • 1978 (8086): 第一款16位Intel处理器,是IBM PC和DOS系统的基础。
    • 1985 (386): 第一款32位Intel处理器 (IA32),引入了“平坦寻址模式”,能够运行Unix等现代操作系统。
    • 2004 (Pentium 4E): 第一款支持64位扩展 (x86-64) 的Intel处理器。
    • 2006 (Core 2): 第一款Intel多核处理器,标志着性能提升的思路从单纯提高主频转向增加核心数。
  • 性能发展的趋势与挑战:
    • CPU-内存性能差距 (The CPU-Memory Gap): 几十年来,处理器性能的提升速度远超内存(DRAM)和磁盘(Disk)。这导致CPU经常需要花费时间等待从内存中获取数据,内存访问延迟成为主要性能瓶颈。
    • 功耗墙 (Power Wall): 大约在2005年之后,由于功耗和散热问题,单纯提高CPU时钟频率变得不再可行。这促使整个行业转向多核架构来继续提升计算性能。

1.2 从C代码到可执行文件

将C源代码文件(.c)编译成一个可执行程序,主要经历以下步骤:

  1. 编译 (Compilation): 编译器(如 gcc -S)将C代码翻译成人类可读的汇编代码(.s文件)。
  2. 汇编 (Assembly): 汇编器(如 as)将汇编代码转换成二进制的机器指令,生成目标文件(.o文件)。此文件包含了代码的二进制表示,但外部函数的地址等尚未确定。
  3. 链接 (Linking): 链接器(如 ld)将多个目标文件和所需的库文件(如C标准库)合并起来,解析符号引用(如函数调用),最终生成一个完整的可执行文件。
  4. 反汇编 (Disassembly): 可以使用工具将目标文件或可执行文件“逆向”回汇编代码,这对于调试和理解底层代码非常有帮助。
    • 命令行工具: objdump -d <文件名>
    • GDB调试器内: disassemble <函数名>

1.3 汇编基础:程序员视角

在汇编层面,程序员直接与处理器的状态打交道。

  • 程序员可见的状态 (Programmer-Visible State):

    • PC Programme Counter (程序计数器): 在x86-64架构中是 %rip 寄存器。它存放着下一条 将要执行的指令的内存地址
    • 寄存器文件 (Register File): 16个64位的通用整数寄存器(如 %rax, %rbx 等),它们是CPU内部极快的小容量存储单元。
    • 条件码 (Condition Codes): 一组特殊的标志位,用于存储最近一次算术或逻辑运算的结果状态(例如,结果是否为零、是否发生进位等)。它们是实现条件分支(如if-else)的基础。
    • 内存 (Memory): 一个巨大的、按字节寻址的数组,用于存放代码、用户数据和程序运行栈。
  • x86-64 整数寄存器:
    x86-64提供了16个64位的通用寄存器。为了向后兼容,前8个寄存器还可以访问它们的低32位、16位和8位部分。

64位 32位 惯例用途
%rax %eax 累加器 / 函数返回值
%rbx %ebx 基址寄存器
%rcx %ecx 计数器
%rdx %edx 数据寄存器
%rsi %esi 源变址寄存器
%rdi %edi 目的变址寄存器
%rsp %esp 栈指针
%rbp %ebp 基址指针
%r8 - %r15 %r8d - %r15d 通用寄存器
  • AT&T vs. Intel 汇编语法: 本课程使用 AT&T 语法,这在Linux/GCC环境中是默认的。
特性 AT&T 风格 (本课程使用) Intel 风格
操作数顺序 指令 源, 目标 指令 目标, 源
寄存器 %前缀, e.g., %rax 无前缀, e.g., rax
立即数 $\text{前缀}, e.g., $0x104 直接书写
内存寻址 8(%rbp) [rbp+8]

1.4 数据传送:mov 指令与寻址模式

  • 指令格式: movq Source, Dest (将8字节数据从源移动到目标)(q代表是64位操作)。

  • 操作数类型 (Operand Types):

    • 立即数 (Immediate): 常数值,以 $ \text{开头,例如} $-533
    • 寄存器 (Register): 16个整数寄存器之一,例如 %rax。不能写给有专用用途的如%rsp
    • 内存 (Memory): 内存中的一个地址,由括号 () 表示。(%rax)以寄存器中存的数据当作地址去内存中寻找。
  • 核心限制: 不可以在一条指令中完成从内存到内存的数据传送。必须先将数据从内存加载到寄存器,再从寄存器存回内存。

  • 内存寻址模式 (Memory Addressing Modes): 用于灵活地指定内存地址。

    • 普通模式 (Normal): (%rcx),将寄存器 %rcx 中的值作为内存地址(等价于C语言中的指针解引用 *rcx)。
    • 偏移模式 (Displacement): D(R),例如 8(%rbp),表示访问内存地址为 Reg[R] + D 的位置(等价于 *(rbp + 8))。
    • 最通用形式: D(Rb, Ri, S)
      • 地址计算公式为: Mem[Reg[Rb] + S * Reg[Ri] + D]
      • D: 常量偏移量 (Displacement)。
      • Rb: 基址寄存器 (Base register)。
      • Ri: 变址寄存器 (Index register)。
      • S: 比例因子 (Scale),值只能是1, 2, 4, 8。这个因子对于数组访问非常有用,可以方便地计算出数组元素的地址。

1.5 算术和逻辑运算

  • 加载有效地址 (leaq):

    • leaq Src, Dst 指令非常特殊。它使用内存寻址的语法来计算一个地址,但 并不会访问内存
    • 相反,它将计算出的 地址值本身 存放到目标寄存器 Dst 中。
    • 编译器经常巧妙地利用它来完成一些高效的算术运算。例如,要计算 x * 12,可以分两步:
      1. leaq (%rdi, %rdi, 2), %rax # rax = x + 2*x = 3*x
      2. salq $2, %rax # rax = rax << 2 = (3*x) * 4 = 12*x
  • 常见的二元运算指令:
    (注意:指令格式为 op Src, Dest,运算结果存回 Dest

指令 运算 描述
addq Src, Dest Dest = Dest + Src 加法
subq Src, Dest Dest = Dest - Src 减法
imulq Src, Dest Dest = Dest * Src 乘法
andq Src, Dest Dest = Dest & Src 按位与
orq Src, Dest Dest = Dest | Src 按位或
xorq Src, Dest Dest = Dest ^ Src 按位异或
salq Src, Dest Dest = Dest << Src 左移
sarq Src, Dest Dest = Dest >> Src 算术右移
shrq Src, Dest Dest = Dest >> Src 逻辑右移
  • 常见的一元运算指令:
指令 运算 描述
incq Dest Dest = Dest + 1 加一
decq Dest Dest = Dest - 1 减一
negq Dest Dest = -Dest 取负 (补码)
notq Dest Dest = ~Dest 按位取反

CSAPP - 03.Machine-Level Programming I
https://yima-gu.github.io/2026/01/14/CSAPP/03-machine-level-1-basics/
作者
Yima Gu
发布于
2026年1月15日
许可协议