struct pt_regs是 Linux 内核中用于保存 CPU 寄存器现场的核心数据结构。当发生系统调用、中断或异常时,CPU 的寄存器状态会被保存到栈上的这个结构体中,内核据此了解“进入内核前用户态在做什么”,并在返回时恢复现场。
核心作用
系统调用:用户态通过
syscall指令进入内核,硬件自动将部分寄存器保存到pt_regs,内核从中读取系统调用号和参数。中断/异常:CPU 触发中断或异常时,硬件和入口汇编代码协作,将寄存器压栈形成
pt_regs,内核处理完后通过它恢复用户态。信号处理:向用户态进程发送信号时,内核修改
pt_regs中的ip、sp等字段,使进程在返回用户态时跳转到信号处理函数。
x86_64 下的结构定义
在 x86_64 架构中,struct pt_regs的定义位于arch/x86/include/asm/ptrace.h,其字段顺序严格对应硬件压栈的顺序(从高地址到低地址):
struct pt_regs { /* * 用户态返回时,部分寄存器由硬件自动压栈(顺序固定): * SS, RSP, RFLAGS, CS, RIP(发生特权级切换时) */ unsigned long r15; unsigned long r14; unsigned long r13; unsigned long r12; unsigned long bp; // RBP unsigned long bx; // RBX unsigned long r11; unsigned long r10; unsigned long r9; unsigned long r8; unsigned long ax; // RAX unsigned long cx; // RCX unsigned long dx; // RDX unsigned long si; // RSI unsigned long di; // RDI unsigned long orig_ax; // 系统调用号(或原始 RAX) unsigned long ip; // RIP(指令指针) unsigned long cs; // 代码段选择子 unsigned long flags; // RFLAGS unsigned long sp; // RSP(栈指针) unsigned long ss; // 栈段选择子 };关键字段解读
| 字段 | 含义 |
|---|---|
ip | 触发异常或系统调用时的指令地址。信号处理时,内核会修改它来跳转到信号处理函数。 |
sp | 用户态栈指针。进入内核时由硬件保存,返回时恢复。 |
flags | RFLAGS 寄存器,包含中断使能、方向标志等。 |
orig_ax | 系统调用号。syscall入口会将原始rax(系统调用号)保存到这里,之后ax可被返回值覆盖。 |
ax~r15 | 通用寄存器。系统调用参数依次存放在di、si、dx、r10、r8、r9中。 |
与user_mode()的关系
内核通过检查pt_regs->cs的最低两位(CPL)来判断异常来自用户态还是内核态:
static inline int user_mode(struct pt_regs *regs) { return (regs->cs & 3) != 0; }这是缺页异常处理中区分“用户地址故障”和“内核地址故障”的关键依据。
在缺页异常处理中的角色
do_user_addr_fault(regs, error_code, address)中的regs就是struct pt_regs *。它提供了:
regs->ip:定位触发缺页的指令(用于is_prefetch检测、show_signal_msg日志)。regs->sp:判断是否涉及栈扩展(VM_GROWSDOWN)。user_mode(regs):决定走用户态还是内核态的故障处理路径。
struct pt_regs是内核与硬件、用户态之间的“寄存器快照”,几乎所有与异常、中断、系统调用相关的代码都围绕它展开。