如何使用在同一CPU上运行的调试器读取CPU寄存器?

Kyl*_*yle 10 c cpu assembly gdb

当我学习汇编时,我使用GDB的方式如下:

gdb ./a.out (a is a compiled C script that only prints hello world)
break main
run
info registers
Run Code Online (Sandbox Code Playgroud)

当我自己使用相同的CPU打印寄存器时,为什么我可以看到程序使用的寄存器?不应该使用GDB(或操作系统)覆盖寄存器,只显示覆盖的寄存器?我能想到的唯一答案是我的CPU是双核的,其中一个内核正在使用,另一个是为程序保留的.

Jes*_*ter 15

操作系统维护每个执行线程的寄存器状态.当您检查gdb中的寄存器时,调试器实际上是要求操作系统从保存的状态读取寄存器值.你的程序在那个时间点没有运行,它是调试器.

假设您的系统上没有其他进程.以下是发生的事情的简化视图:

  1. 调试器启动并获取cpu
  2. 调试器要求操作系统加载您的程序
  3. 调试器要求操作系统放置断点
  4. 调试器要求操作系统开始执行程序.操作系统保存gdb寄存器状态并将控制权转移到您的程序.
  5. 你的程序遇到了断点.操作系统控制,保存程序的寄存器状态,重新加载gdb寄存器并将cpu返回给gdb.
  6. 调试器要求操作系统从保存的状态读取程序的寄存器.

请注意,此机制是多任务操作系统的正常职责的一部分,它不是特定于调试.当OS调度程序决定应该执行另一个程序时,它会保存当前状态并加载另一个程序.这称为上下文切换,它可能每秒发生多次,给人一种错觉,即即使你只有一个cpu核心,程序也会同时执行.


Bod*_*sen 5

在过去的单任务操作系统中,唯一可能阻碍程序执行的因素是中断.现在,中断处理程序遇到的问题与你所说的相同,你的程序正在计算某些东西,用户按下一个键 - 中断 - 中断服务程序必须做一些工作,但不能修改过程中的单个寄存器.这是主要原因,堆栈是首先发明的.通常的80x86 DOS中断服务程序如下所示:

push ax
push cx
push dx
push bx
push si
push di
push bp
// no need to push sp
[do actual work, caller registers avaiable on stack if needed]
pop bp
pop di
pop si
pop bx
pop dx
pop cx
pop ax
iret
Run Code Online (Sandbox Code Playgroud)

这甚至是如此常见,以至于创建了一个新的指令对pushapopa(用于push/pop all)以简化此任务.

在今天的操作系统和应用程序之间具有地址空间隔离的CPU中,CPU提供一些任务状态系统并允许操作系统切换任务(中断可能仍然类似于上面概述的工作,但也可以通过任务切换来处理).所有现代操作系统都使用这个任务状态系统的系统,其中CPU在没有被主动执行时保存进程的所有寄存器.就像Jester已经解释的那样,gdb只是询问操作系统在要调试的进程上的这些值,然后打印它们.