GDB如何暂停执行


16

您可能知道,我们可以使用GDB并在代码上设置断点来暂停执行以进行调试。

我的问题是,GDB如何暂停进程,并允许您使用i r例如查看寄存器的内容。这些寄存器不是经常被其他OS进程使用吗?他们怎么不被覆盖?

它只是内容的快照,而不是实时数据吗?


2
当操作系统决定暂停您的程序并运行另一个程序时,为什么所有寄存器都不会被覆盖?
user253751 '19

CppCon 2018:Simon Brand“ C ++调试器的工作原理” youtube.com/watch?v=0DDrseUomfU
Robert Andrzejuk

Answers:


24

它随体系结构而略有不同,但是要点几乎普遍适用:

  • 中断服务使CPU状态(包括寄存器)在运行ISR之前保存到内存,并在ISR退出时恢复。

  • 如果中断服务程序交换了保存这些寄存器的内存位置的内容,则可以执行上下文切换。每个线程都有一个内存区域,该线程不在运行时会在其中保存其寄存器。

  • 上下文切换由线程调度器控制,该调度器考虑线程是否正在等待I / O,同步,其优先级是什么,信号传递等。通常会有一个挂起计数纳入其中。

  • 调试器可以增加挂起计数,以确保线程不可运行。然后,它可以检查(并更改)线程的已保存寄存器副本。


14

除了@BenVoigt提供的大量信息之外,请允许我补充一些内容:

调试器通过在与要中断的所需(源)行相对应的代码位置上,使用特定陷阱指令替换正在调试的进程中的机器代码值(指令或指令的一部分)来设置断点。该特定陷阱指令旨在用作断点-调试器知道这一点,操作系统也知道。

当正在调试的进程/线程命中陷阱指令时,将触发描述该进程的@Ben,其中包括上下文交换的一半,该上下文交换将挂起当前正在运行的线程(包括将其CPU状态保存到内存中),以供以后重新使用。由于此陷阱是一个断点陷阱,因此操作系统可能使用@Ben描述的机制使正在调试的进程保持挂起状态,并通知并最终恢复调试器。

调试器使用系统调用,然后访问被调试的挂起进程/线程的保存状态。

要执行(恢复)已中断的代码行(现在具有特定的陷阱指令),调试器将恢复使用断点陷阱指令覆盖的原始机器代码值,还可能在其他地方设置另一个陷阱(例如,单步执行,或用户设置新的断点),然后将进程/线程标记为可运行,可能使用@Ben描述的机制。

实际细节可能更复杂,因为保持命中的长时间运行的断点意味着要做一些事情,例如将断点陷阱换为实际代码,以便行可以运行,然后再次将断点换回。

这些寄存器不是经常被其他OS进程使用吗?他们怎么不被覆盖?

如@Ben所述,使用已经存在的线程挂起/恢复功能(多任务的上下文切换/交换),该功能允许使用时间分片由多个进程/线程共享处理器。

它只是内容的快照,而不是实时数据吗?

两者都是。由于命中断点的线程已被挂起,因此在挂起时它是实时数据(CPU寄存器等)的快照,并且如果恢复该线程,则将要恢复到处理器中的CPU寄存器值权威主机。如果您使用调试器的用户界面读取和/或更改(正在调试的进程的)CPU寄存器,它将使用系统调用读取和/或更改此快照/主机。


1
好吧,大多数处理器体系结构都支持调试陷阱,例如,当IP(指令指针)等于断点寄存器中存储的地址时触发,从而节省了重写代码的需要。(通过匹配IP以外的其他寄存器,您可以获取数据断点,并且通过在每条指令后进行陷阱捕获,就可以实现单步执行)当然,只要代码不在只读存储器中,您描述的内容也是可以的。
Ben Voigt

关于上一段中的“如果您更改了CPU寄存器...”,我想您的意思是“如果您更改了CUP寄存器的已保存副本...”那么当操作系统恢复该过程时,被更改的数据将被写回到实际的寄存器。
jamesqf

@jamesqf,是的,谢谢!
埃里克·艾德

@BenVoigt,表示同意。尽管调试器可以处理无限数量的断点,但是硬件可以处理零个或几个断点,因此调试器必须进行一些调整。
埃里克·艾德

@jamesqf:形容为副本有点误导。它是线程未运行时线程状态的官方存储。
Ben Voigt,

5

严格来讲,至少在大多数典型情况下,gdb本身不会暂停执行。而是gdb询问操作系统,然后操作系统暂停执行。

最初看起来似乎是没有区别的区别-但老实说,确实存在区别。区别在于:该功能已经内置在典型的操作系统中,因为它必须无论如何都必须能够暂停并重新开始执行线程-在未计划运行线程的情况下(例如,它需要一些资源来(当前不可用),操作系统需要暂停它,直到可以计划运行为止。

为此,操作系统通常为每个线程留出一块内存,以保存计算机的当前状态。当需要暂停线程时,计算机的当前状态将保存在该区域中。需要恢复线程时,将从该区域恢复计算机的状态。

当调试器需要暂停线程时,它会以与其他原因完全相同的方式使操作系统暂停该线程。然后,要读取已暂停线程的状态,调试器将查看线程的已保存状态。如果修改状态,调试器将写入保存的状态,然后在线程恢复时生效。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.