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 |
用户模式或超级模式的异常入口地址寄存器。 |
walk 和 walkaddr 与 MMU 的功能重叠 MMU 的核心功能是自动进行虚拟地址到物理地址的转换,而 walk 和 walkaddr 提供了类似的功能,但有以下不同之处:
相似点 虚拟地址到物理地址的解析: MMU 和 walk/walkaddr 都需要逐级解析页表。 页表格式一致: 使用相同的三级页表结构(Sv39 模式)。 地址转换依赖页表: 都基于页表的内容进行虚拟地址到物理地址的转换。 不同点 实现层级: MMU 是硬件实现的,自动解析虚拟地址到物理地址。 walk 和 walkaddr 是内核代码,提供软件接口,供操作系统手动解析地址。 作用场景: MMU 是在程序运行时动态翻译地址(运行时翻译)。 walk 和 walkaddr 用于页表管理,比如检查、分配和更新页表。 功能扩展: walk 支持动态分配新页表(alloc 参数),这是 MMU 无法直接完成的。 walkaddr 包含权限检查(如 PTE_U 和 PTE_V),用于验证地址有效性。
MMU 的功能和优势 MMU 的硬件功能无法被简单的软件实现完全替代,尤其在性能和实时性方面:
性能: MMU 在硬件层直接完成地址翻译,速度远快于软件。 实时翻译: MMU 动态翻译地址,无需操作系统干预。 TLB(Translation Lookaside Buffer): 硬件提供缓存机制(TLB),大幅加速地址翻译。 保护机制: MMU 提供内存保护(如权限检查),防止用户程序非法访问。
MMU 集成在 CPU 内部,与计算单元(ALU)、寄存器和缓存共享同一芯片。 在现代处理器中,MMU 是 CPU 核心不可或缺的一部分,用于处理虚拟内存地址翻译和权限管理。
TLB (Translation Lookaside Buffer): 是 MMU 的一部分,用于缓存最近的虚拟地址到物理地址的映射。 减少频繁访问页表带来的性能开销。
CPU 的寄存器是高速存储器,用于在处理指令时存储临时数据。根据功能和用途,它们可以分为以下几类:
通用寄存器 (General Purpose Registers, GPRs) 作用:存储临时数据、操作数和中间计算结果。 特点: 灵活多用途,可以存储任意类型的数据。 数量和命名随架构不同而变化(如 x86 有 EAX, EBX, ARM 有 R0-R31)。 常见用途: 操作数存储:加法、乘法等指令需要操作数,通常存放在 GPR 中。 中间结果存储:指令执行的中间计算结果保存在 GPR 中。 指针或索引:用于存放内存地址或数组索引。
专用寄存器 (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): 作用:专门处理浮点数运算。
控制寄存器 (Control Registers) 作用:控制和配置 CPU 的工作状态。 常见类型: CR0-CR4 (x86): CR0:控制 CPU 的工作模式(如保护模式或实模式)。 CR3:存放页表基地址,用于虚拟内存管理。 SATP (Supervisor Address Translation and Protection) (RISC-V): 存放页表基地址,控制地址翻译。 状态寄存器 (Status Registers): 记录 CPU 的当前状态(如中断开关、处理器模式等)。
浮点寄存器 (Floating-Point Registers) 作用:处理浮点数相关的计算。 特点: 用于保存浮点数操作数和结果。 示例:x86 架构中的 ST0-ST7(浮点栈)。 在现代 CPU(如 x86-64 和 ARM)中,SSE 或 NEON 等指令集扩展也用浮点寄存器处理向量运算。
向量寄存器 (Vector Registers) 作用:用于 SIMD(单指令多数据)计算,加速向量和矩阵操作。 特点: 每个寄存器可以存储多个数据元素(如 4 个浮点数)。 示例: x86:XMM, YMM, ZMM(分别对应 SSE, AVX, AVX-512 指令集)。 ARM:V0-V31(NEON 指令集)。
堆栈相关寄存器 作用:用于支持函数调用、递归、局部变量存储等。 主要寄存器: 堆栈指针 (SP):指向栈顶。 基址指针 (BP):指向栈帧基址。 链接寄存器 (Link Register, LR): 在 ARM 架构中用于存储子程序返回地址。
系统寄存器 (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 调用约定。