Registers

各寄存器及用法

  • stvec(supervisor trap vector base address), 最后两个bit位为模式位,内核将其处理的trap handler写入此寄存器,

  • sepc(supervisor exception program counter), 将当前的指令地址(即程序计数器pc,程序计数器位一个寄存器,存储着吓一跳将要执行的指令地址)写入到sepc,由于要产生trap,因此要暂停当前代码的执行,stvec的值会覆盖pc从而开始trap handler的处理

  • sret(supervisor return)寄存器sepc的值复制到pc,恢复执行

  • scause, 数字描述陷阱的原因

  • sscratch,保存用户寄存器之前其值被新的操作覆盖,csrrw t0, sscratch, t1 # 保存 t1 到 sscratch,并将 sscratch 的值存到 t0

stvec(Supervisor Trap Vector) 存储陷入向量的起始地址。在 xv6 中,它被设置为 uservec。 sepc(Supervisor Exception Program Counter) 存储发生陷入时的用户态 PC 地址。 sscratch 临时保存寄存器值,例如用户传递的 a0,便于 uservec 使用。 sstatus 管理 CPU 当前的模式、陷入类型、全局中断使能等。

SATP

  • SATP(Supervisor Address Translation and Protection)寄存器 是一个重要的控制寄存器,主要用于管理虚拟地址到物理地址的转换以及内存保护机制。每个CPU核通常都会拥有一个独立的SATP寄存器,因为它直接影响当前核的地址空间设置。

  • SATP寄存器的某些字段用于存储页表的根节点物理地址,指向用于虚拟地址到物理地址映射的页表。

  • SATP寄存器的具体结构根据RISC-V的位宽和地址转换模式不同而有所变化。例如,在64位RISC-V架构(支持Sv39或Sv48)下,SATP寄存器可能如下:

位范围

名称

描述

63-60

MODE

地址转换模式,指定虚拟内存的使用模式。

59–44 (16位)

ASID

地址空间标识符,用于多任务环境下区分进程。

43-0

PPN

页表的物理页号(根页表物理地址的高位)

Mode字段的含义

MODE 值

名称

描述

0

Bare

禁用虚拟内存,直接使用物理地址。

8

Sv39

使用39位虚拟地址,支持三级页表(常用于大多数64位系统)。

9

Sv48

使用48位虚拟地址,支持四级页表(支持更大的地址空间)。

10

Sv57

使用57位虚拟地址(未来扩展,当前较少实现)。

其他值

未定义

保留,可能触发异常或未定义行为。

在不同模式下,PPN的长度可能会有所变化。

// use riscv's sv39 page table scheme.
#define SATP_SV39 (8L << 60)//是用Sv39(8)的mode

#define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12))//将pagetable转化位PPN,去掉低12位页内地址

地址翻译过程需要将虚拟地址和SATP相结合以实现翻译。

控制状态寄存器(CSR, Control and Status Registers)

CSR 名称

功能

mstatus

机器模式的状态寄存器(全局中断使能等)。

mepc

异常返回地址寄存器。

mcause

异常原因寄存器(中断或异常代码)。

sscratch

临时存储寄存器,供操作系统或异常处理使用。

stvec

用户模式或超级模式的异常入口地址寄存器。

  1. walk 和 walkaddr 与 MMU 的功能重叠 MMU 的核心功能是自动进行虚拟地址到物理地址的转换,而 walk 和 walkaddr 提供了类似的功能,但有以下不同之处:

相似点 虚拟地址到物理地址的解析: MMU 和 walk/walkaddr 都需要逐级解析页表。 页表格式一致: 使用相同的三级页表结构(Sv39 模式)。 地址转换依赖页表: 都基于页表的内容进行虚拟地址到物理地址的转换。 不同点 实现层级: MMU 是硬件实现的,自动解析虚拟地址到物理地址。 walk 和 walkaddr 是内核代码,提供软件接口,供操作系统手动解析地址。 作用场景: MMU 是在程序运行时动态翻译地址(运行时翻译)。 walk 和 walkaddr 用于页表管理,比如检查、分配和更新页表。 功能扩展: walk 支持动态分配新页表(alloc 参数),这是 MMU 无法直接完成的。 walkaddr 包含权限检查(如 PTE_U 和 PTE_V),用于验证地址有效性。

  1. MMU 的功能和优势 MMU 的硬件功能无法被简单的软件实现完全替代,尤其在性能和实时性方面:

性能: MMU 在硬件层直接完成地址翻译,速度远快于软件。 实时翻译: MMU 动态翻译地址,无需操作系统干预。 TLB(Translation Lookaside Buffer): 硬件提供缓存机制(TLB),大幅加速地址翻译。 保护机制: MMU 提供内存保护(如权限检查),防止用户程序非法访问。

MMU 集成在 CPU 内部,与计算单元(ALU)、寄存器和缓存共享同一芯片。 在现代处理器中,MMU 是 CPU 核心不可或缺的一部分,用于处理虚拟内存地址翻译和权限管理。

TLB (Translation Lookaside Buffer): 是 MMU 的一部分,用于缓存最近的虚拟地址到物理地址的映射。 减少频繁访问页表带来的性能开销。

CPU 的寄存器是高速存储器,用于在处理指令时存储临时数据。根据功能和用途,它们可以分为以下几类:

  1. 通用寄存器 (General Purpose Registers, GPRs) 作用:存储临时数据、操作数和中间计算结果。 特点: 灵活多用途,可以存储任意类型的数据。 数量和命名随架构不同而变化(如 x86 有 EAX, EBX, ARM 有 R0-R31)。 常见用途: 操作数存储:加法、乘法等指令需要操作数,通常存放在 GPR 中。 中间结果存储:指令执行的中间计算结果保存在 GPR 中。 指针或索引:用于存放内存地址或数组索引。

  2. 专用寄存器 (Special Purpose Registers) 作用:用于特定任务,支持 CPU 的内部控制和状态管理。 常见类型: 程序计数器 (Program Counter, PC): 作用:存储下一条指令的地址。 指令执行后,自动递增或根据跳转指令修改。 堆栈指针 (Stack Pointer, SP): 作用:指向当前栈顶,用于函数调用、参数传递和局部变量管理。 栈操作(如 PUSH 和 POP)会修改堆栈指针。 基址寄存器 (Base Pointer, BP): 作用:在函数调用中保存栈基地址,便于访问局部变量。 标志寄存器 (Flags Register, FR): 作用:存储算术和逻辑运算的状态(如零标志、进位标志)。 示例:x86 中的 EFLAGS 包括 ZF(零标志)、CF(进位标志)、OF(溢出标志)等。 指令寄存器 (Instruction Register, IR): 作用:保存当前正在执行的指令。 浮点寄存器 (Floating-Point Registers): 作用:专门处理浮点数运算。

  3. 控制寄存器 (Control Registers) 作用:控制和配置 CPU 的工作状态。 常见类型: CR0-CR4 (x86): CR0:控制 CPU 的工作模式(如保护模式或实模式)。 CR3:存放页表基地址,用于虚拟内存管理。 SATP (Supervisor Address Translation and Protection) (RISC-V): 存放页表基地址,控制地址翻译。 状态寄存器 (Status Registers): 记录 CPU 的当前状态(如中断开关、处理器模式等)。

  4. 浮点寄存器 (Floating-Point Registers) 作用:处理浮点数相关的计算。 特点: 用于保存浮点数操作数和结果。 示例:x86 架构中的 ST0-ST7(浮点栈)。 在现代 CPU(如 x86-64 和 ARM)中,SSE 或 NEON 等指令集扩展也用浮点寄存器处理向量运算。

  5. 向量寄存器 (Vector Registers) 作用:用于 SIMD(单指令多数据)计算,加速向量和矩阵操作。 特点: 每个寄存器可以存储多个数据元素(如 4 个浮点数)。 示例: x86:XMM, YMM, ZMM(分别对应 SSE, AVX, AVX-512 指令集)。 ARM:V0-V31(NEON 指令集)。

  6. 堆栈相关寄存器 作用:用于支持函数调用、递归、局部变量存储等。 主要寄存器: 堆栈指针 (SP):指向栈顶。 基址指针 (BP):指向栈帧基址。 链接寄存器 (Link Register, LR): 在 ARM 架构中用于存储子程序返回地址。

  7. 系统寄存器 (System Registers) 作用:用于控制和管理 CPU 的系统功能。 示例: 中断描述表寄存器 (IDTR): 存放中断向量表的基地址。 任务状态段寄存器 (TR): 存储任务的状态信息,支持任务切换。 总结 寄存器类别 主要作用 通用寄存器 存储临时数据、操作数和中间结果。 专用寄存器 执行特定任务,如程序计数、堆栈管理、标志状态。 控制寄存器 控制 CPU 工作模式、配置内存管理。 浮点寄存器 处理浮点数运算。 向量寄存器 支持 SIMD 运算,加速向量和矩阵计算。 堆栈相关寄存器 支持函数调用和局部变量存储。 系统寄存器 管理 CPU 系统功能,如中断和任务切换。 这些寄存器共同支持了 CPU 的高效运行,满足了不同的计算需求。

在函数调用过程中,处理器的寄存器分为两类:caller-save registers 和 callee-save registers。这两类寄存器的不同之处在于谁负责保存和恢复寄存器的值。在函数调用约定(calling convention)中,这两类寄存器的行为有所规定,下面是它们的定义和区别。

Callee-Save Registers(被调用者保存寄存器) 定义:被调用函数(callee)必须保存(保存现场)在它使用之前的寄存器值,并在返回之前恢复这些寄存器的值。换句话说,如果被调用的函数需要使用这些寄存器,它需要在使用之前保存这些寄存器的原值,并在返回之前恢复。 职责:被调用函数负责保存和恢复这些寄存器的值,以确保返回调用函数时这些寄存器的内容没有改变。 示例: 在 x86 架构中,EBX, ESI, EDI 等寄存器通常是被调用者保存寄存器(callee-save)。 在 x86-64 的 System V ABI 中,RBP, RBX, R12, R13, R14, R15 等寄存器是被调用者保存的寄存器。 在 ARM 的 AAPCS 约定中,R4 到 R11 被视为被调用者保存寄存器。

Caller-Save Registers(调用者保存寄存器) 定义:调用函数(caller)负责保存和恢复寄存器的值。在函数调用前,调用者需要检查是否需要保留某些寄存器的值,如果需要,调用者必须在调用函数之前保存这些寄存器的值。在调用返回后,调用者负责恢复这些寄存器的值。 职责:调用函数负责保存和恢复这些寄存器的内容,以保证被调用函数对这些寄存器的修改不会影响到调用者的状态。 示例: 在 x86 架构中,EAX, ECX, EDX 等寄存器通常是调用者保存寄存器(caller-save)。 在 x86-64 System V ABI 中,RAX, RCX, RDX, R8, R9, R10, R11 是调用者保存寄存器。 在 ARM 的 AAPCS 约定中,R0 到 R3 是调用者保存寄存器。

如果每次调用都要求被调用函数保存所有寄存器的值,开销会很大。通过将一些寄存器标记为调用者寄存器,只有在需要时调用者才负责保存这些寄存器,减少了不必要的保存和恢复操作。

操作系统和处理器架构的 ABI(应用二进制接口)

ABI 定义了函数如何调用,包括: 参数如何传递(通过寄存器还是堆栈)。 返回值如何传递(通常通过寄存器)。 调用和返回时堆栈的管理。 寄存器的保存和恢复规则。 堆栈对齐方式等。 每种处理器架构(如 x86, x86-64, ARM)通常会有自己的标准 ABI,而操作系统(如 Windows, Linux, macOS)会根据这个 ABI 定义函数调用的规范。例如,x86 架构上有 cdecl, stdcall, thiscall 等调用约定,而 x86-64 上有 System V ABI(Linux/Unix)和 Microsoft x64 调用约定。