您可能知道,我们可以使用GDB并在代码上设置断点来暂停执行以进行调试。
我的问题是,GDB如何暂停进程,并允许您使用i r
例如查看寄存器的内容。这些寄存器不是经常被其他OS进程使用吗?他们怎么不被覆盖?
它只是内容的快照,而不是实时数据吗?
您可能知道,我们可以使用GDB并在代码上设置断点来暂停执行以进行调试。
我的问题是,GDB如何暂停进程,并允许您使用i r
例如查看寄存器的内容。这些寄存器不是经常被其他OS进程使用吗?他们怎么不被覆盖?
它只是内容的快照,而不是实时数据吗?
Answers:
除了@BenVoigt提供的大量信息之外,请允许我补充一些内容:
调试器通过在与要中断的所需(源)行相对应的代码位置上,使用特定陷阱指令替换正在调试的进程中的机器代码值(指令或指令的一部分)来设置断点。该特定陷阱指令旨在用作断点-调试器知道这一点,操作系统也知道。
当正在调试的进程/线程命中陷阱指令时,将触发描述该进程的@Ben,其中包括上下文交换的一半,该上下文交换将挂起当前正在运行的线程(包括将其CPU状态保存到内存中),以供以后重新使用。由于此陷阱是一个断点陷阱,因此操作系统可能使用@Ben描述的机制使正在调试的进程保持挂起状态,并通知并最终恢复调试器。
调试器使用系统调用,然后访问被调试的挂起进程/线程的保存状态。
要执行(恢复)已中断的代码行(现在具有特定的陷阱指令),调试器将恢复使用断点陷阱指令覆盖的原始机器代码值,还可能在其他地方设置另一个陷阱(例如,单步执行,或用户设置新的断点),然后将进程/线程标记为可运行,可能使用@Ben描述的机制。
实际细节可能更复杂,因为保持命中的长时间运行的断点意味着要做一些事情,例如将断点陷阱换为实际代码,以便行可以运行,然后再次将断点换回。
这些寄存器不是经常被其他OS进程使用吗?他们怎么不被覆盖?
如@Ben所述,使用已经存在的线程挂起/恢复功能(多任务的上下文切换/交换),该功能允许使用时间分片由多个进程/线程共享处理器。
它只是内容的快照,而不是实时数据吗?
两者都是。由于命中断点的线程已被挂起,因此在挂起时它是实时数据(CPU寄存器等)的快照,并且如果恢复该线程,则将要恢复到处理器中的CPU寄存器值的权威主机。如果您使用调试器的用户界面读取和/或更改(正在调试的进程的)CPU寄存器,它将使用系统调用读取和/或更改此快照/主机。
严格来讲,至少在大多数典型情况下,gdb本身不会暂停执行。而是gdb询问操作系统,然后操作系统暂停执行。
最初看起来似乎是没有区别的区别-但老实说,确实存在区别。区别在于:该功能已经内置在典型的操作系统中,因为它必须无论如何都必须能够暂停并重新开始执行线程-在未计划运行线程的情况下(例如,它需要一些资源来(当前不可用),操作系统需要暂停它,直到可以计划运行为止。
为此,操作系统通常为每个线程留出一块内存,以保存计算机的当前状态。当需要暂停线程时,计算机的当前状态将保存在该区域中。需要恢复线程时,将从该区域恢复计算机的状态。
当调试器需要暂停线程时,它会以与其他原因完全相同的方式使操作系统暂停该线程。然后,要读取已暂停线程的状态,调试器将查看线程的已保存状态。如果修改状态,调试器将写入保存的状态,然后在线程恢复时生效。