5

我正在C++中创建一个并发内存回收算法。定期执行mutator线程的堆栈需要检查,以便我可以看到线程当前拥有哪些引用。在这个过程中,我还需要检查mutator线程的寄存器以检查可能存在的任何引用。如何停止线程并将其寄存器清空到堆栈中?

很明显,许多JVM和C#虚拟机在做垃圾收集周期的过程中没有问题。但是,我一直无法找到这个问题的确切解决方案。

为了检查根集(如果可以的话)(或者知道它是如何完成的),我无法弄清楚Bohem垃圾回收器正在发生什么,我真的很想知道。

理想情况下,我将能够使中断线程中断,并执行一段处理程序代码,它会报告它是PC,并将基于寄存器的引用清空到堆栈中,然后可能有助于完成收集周期。我相信大多数系统中的大多数编译器会在调用中断或信号处理程序时自动刷新寄存器,但我不清楚具体情况或如何访问这些数据。看起来单独的堆栈可能用于中断和信号处理程序。另外,我找不到有关如何定位特定线程或如何发送信号的任何信息。 Windows似乎并不支持这种信号形式,并且我希望我的系统能够在x86-64处理器上的Linux和Windows上运行。

编辑:在某些情况下使用SuspendThread(),尽管安全点似乎是首选。任何想法为什么?有没有办法处理持久的I/O等待或其他等待内核代码返回的方法?

+0

不使用刷新,收集器只是检查寄存器中的对象引用。使用GetThreadContext()可以在Windows中轻松完成。 – 2012-01-10 04:58:40

+0

好吧,'GetThreadContext'完全不读取寄存器。它读取内存寄存器保存到最后一次上下文切换使该特定线程无效的时间。 – 2012-01-10 06:25:04

+0

@coolkid:编译器在中断发生时不会刷新寄存器(也不会发出代码),这是CPU本身的一个特性。 – 2012-01-10 06:26:07

回答

3

我认为这是一个非常有趣的问题,所以我深入了解了一下。事实证明,热点JVM使用一种称为“安全点”的机制,它使JVM的线程协作地全部停止,以便GC可以开始。换句话说,启动GC的线程不会强行停止其他线程,其他线程会通过各种巧妙的机制自动挂起。

我不相信JVM扫描寄存器,因为定义了一个安全点,使得所有的根都是已知的(我认为这意味着在内存中)。

更多信息参见:

关于你希望“中断”所有线程,根据幻灯片I如上所述,线程挂起在Solaris和Linux上是“不可靠的,例如虚假信号”。我不确定幻灯片所指的线程暂停有甚么机制。

+0

嗯..如果是这样的话,那么你如何处理这样一个事实:如果一个线程等待IO或者操作系统只是有很多竞争进程,那么线程可能不会被安排一段时间?我不愿意为此而拖延。 – coolkid 2012-01-10 06:28:52

+0

如果您查看了上面的safepoint.cpp链接,那么注释表示任何当前被阻止的线程将不会被允许继续,直到safepoint操作完成(所以它就像他们已经处于安全点)。我猜测所有可运行的线程必须在GC开始之前安排好,以便它们可以在已知状态下暂停,可能没有办法。 – 2012-01-10 07:02:29

+0

Yikes!如果其中一人正在等待IO或正在睡觉等待锁定被释放,那可能会很长时间。任何人都知道这是真的吗?关于.NET的GC的 – coolkid 2012-01-10 07:05:32

1

在windows上,您应该可以使用SuspendThread(和ResumeThread)以及GetThreadContext(如Hans提到的)。所有这些函数都会处理您想要定位的特定线程。

要获得当前进程中所有线程的列表,请参阅this(toolhlp32在x64上工作,尽管其命名方式很糟糕......)。

作为感兴趣的一点,将寄存器刷新到x86上的堆栈的一种方法是使用PUSHAD汇编指令。

+0

将寄存器清除到堆栈的“常用”方法是通过中断。 – 2012-01-10 06:17:58

+0

@BenVoigt:afaiak窗口不提供内核模式以外的内容(我可能是错的)。 – Necrolis 2012-01-10 06:19:01

+0

这是从用户模式访问内核服务的传统方式,是使用软件中断。 x86现在有'sysenter'指令,它可以简化一些事情,但我相信它仍然执行相同的寄存器保存。但是,真的,我只是在评论你的陈述,“PUSHAD”是将寄存器保存到堆栈的唯一方法,因为它不是。顺便说一下,上下文切换是由中断(I/O或定时器)引起的,这就是线程状态如何在内存中为'GetThreadContext'访问。 – 2012-01-10 06:21:45

相关问题