寄存器查看与修改——通用寄存器、段寄存器、EFLAGS 和 CR 系列
info registers:一次性看全部
bochs:1> info registers
输出示例:
CPU0:
rax=0000000000000000 rbx=0000000000000000 rcx=0000000000000000
rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
rbp=0000000000000000 rsp=0000000000000000
r8=0000000000000000 r9=0000000000000000 r10=0000000000000000
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
rip=0000000000007c00
eflags=00000002
iopl=0 nv up ei pl nz na po nc
cs=0000 ds=0000 es=0000 ss=0000 fs=0000 gs=0000
gdt=0000000000000000 gdtr=0000000000000000
idt=0000000000000000 idtr=0000000000000000
ldt=0000000000000000 ldtr=0000000000000000
tr =0000000000000000
dr0=0000000000000000 dr1=0000000000000000
dr2=0000000000000000 dr3=0000000000000000
dr6=0000000000000000 dr7=0000000000000000
cr0=00000010 cr2=00000000 cr3=00000000
cr4=00000000
寄存器分几类
| 类别 | 寄存器 | OS 开发关注点 |
|---|---|---|
| 通用 | RAX-R15 | 函数参数、返回值、临时变量 |
| 指令指针 | RIP | 当前执行位置 |
| 栈指针 | RSP / RBP | 栈帧结构 |
| 段寄存器 | CS/DS/ES/SS/FS/GS | 段基址、段限、权限(保护模式) |
| 控制寄存器 | CR0-CR4 | 分页开关、保护模式开关、PAE |
| 调试寄存器 | DR0-DR7 | 硬件断点(内核调试用) |
| 表寄存器 | GDTR/IDTR/LDTR/TR | 段描述符表、中断描述符表 |
| 标志 | EFLAGS | 进位/零/溢出/中断开关 |
OS 开发最关注的几个寄存器
EIP / RIP:当前正在执行的指令位置。断点、单步都围绕它。
CR0:最重要的控制寄存器。位含义:
| CR0 位 | 名称 | 作用 |
|---|---|---|
| PE (bit 0) | Protection Enable | 0 = 实模式,1 = 保护模式 |
| MP (bit 1) | Monitor Coprocessor | FPU 存在标志 |
| EM (bit 2) | Emulate | 1 = 模拟 FPU(OS 自己处理) |
| TS (bit 3) | Task Switched | 每次任务切换置位,触发 FPU 异常 |
| ET (bit 4) | Extension Type | 80387/80287 |
| NE (bit 5) | Numeric Error | 1 = 用内部 FPU 错误处理 |
| WP (bit 16) | Write Protect | 1 = 内核写只读页也触发异常 |
| PG (bit 31) | Paging | 0 = 不分页,1 = 开启分页 |
内核初始化流程就是逐步打开 CR0 的 PE → PG 等位。调试器里观察 CR0 变化能验证你的模式切换代码有没有问题。
CR3:页表根。内核每次切换页表(进程切换)都会改 CR3。调试"进程切换时页表没换对"这类问题,在 CR3 上设 watchpoint 就能抓到。
EFLAGS:16 个标志位,OS 开发常看:
| 位 | 名称 | 含义 |
|---|---|---|
| CF (0) | Carry Flag | 无符号进位 |
| PF (2) | Parity Flag | 奇偶校验 |
| ZF (6) | Zero Flag | 结果为 0 |
| SF (7) | Sign Flag | 结果为负 |
| IF (9) | Interrupt Flag | 1 = 开中断 |
| TF (8) | Trap Flag | 1 = 单步陷阱(调试用) |
段寄存器(保护模式下):实模式下段寄存器就是基址左移 4 位。保护模式下段寄存器是选择子(selector)——指向 GDT 或 LDT 里的段描述符。调试器的 info sreg 可以同时显示选择子、段基址、段限、DPL:
bochs:1> info sreg
cs=0010 ds=0018 es=0018 ss=0018 fs=0000 gs=0000
CS: 0x0010: 32bit data segment, base=0x00000000, limit=0x000fffff
DPL=0, accessed=0, writable=0, expand_down=0
DS: 0x0018: 32bit data segment, base=0x00000000, limit=0xffffffff
DPL=0, accessed=0, writable=1, expand_down=0
修改寄存器
语法:set $<name> = <value>
bochs:1> set $eax = 0x42 # 设置 EAX
bochs:2> set $eip = 0x7c00 # 把执行指针跳回 0x7c00(比如想重新执行 Bootloader)
bochs:3> set $cr0 = 0x00000010 # 关掉分页(PG 位清零)
bochs:4> set $if = 1 # 打开中断(EFLAGS 的 IF 位置 1)
set $eip = value 特别有用——可以让模拟"回到过去"重新跑一段代码,省了重启 Bochs 的时间。
修改 EFLAGS 标志位
bochs:1> set $zf = 1 # 设置 Zero Flag
bochs:2> set $cf = 0 # 清除 Carry Flag
bochs:3> set $if = 0 # 关中断
32 位 vs 64 位寄存器
32 位模式下 Bochs 调试器显示 EAX/EBX/... 等 32 位寄存器名。64 位模式下显示 RAX/RBX/... 等完整 64 位寄存器名。
info registers 会自动根据当前 CPU 模式调整输出格式。不用手动切换。
如果想强制看 32 位视图(即使 64 位模式),可以用 info registers eflags 或 info registers cr0 等过滤器只看特定寄存器。
info msr:Model-Specific Registers
某些 CPU 特性(SYSCALL/SYSRET、FS/GS 基址、TSC)通过 MSR 控制。调试器可以列出:
bochs:1> info msr
输出会列出所有当前有值的 MSR(通常只显示内核写过的那些)。对于调试 x86-64 内核的 SYSCALL 路径或 FS/GS 设置,info msr 是关键工具。
info port:I/O 端口查看
bochs:1> info port 0x3f8 # COM1 串口基址
bochs:2> info port 0xcf8 # PCI 地址端口
bochs:3> info port 0x220 # SB16 默认基址
某些 OS 内核(特别是早期版本)通过 I/O 端口和硬件通信——调试端口读写时这个命令很有用。
小结:OS 开发的寄存器速查表
| 需求 | 命令 |
|---|---|
| 看当前指令位置 | info registers eip |
| 看页表根(CR3) | info cr |
| 切换保护模式 / 分页 | info cr 看 CR0 |
| 看是否开中断 | info flags 看 IF |
| 看 CS/DS/SS 段基址 | info sreg |
| 强制重置 PC | set $eip = 0x7c00 |
| 关中断 | set $if = 0 |