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

相關問題