我認爲應該有可能做一些事情,以接近你所要求的一些小伎倆。
我將描述我認爲最簡單,最有效,但最不靈活的方法,並將其稱爲方法A。使用這種方法的數據必須被佈置在塊組,並且每個塊必須完全在單個頁面內包含:
創建W上的讀/寫圖和寫入時複製視圖用於R,使用相同的文件映射對象。
每當W想要修改數據塊時,它首先在寫時複製視圖中對相應的塊執行虛擬寫入。
注意:寫入頁面會導致寫入時複製,即使寫入並未真正改變內容,但爲了安全起見,我建議避免這種假設,您可以通過包含每個數據塊中的虛擬字節,即R將忽略的一個虛擬字節。 W然後可以增加虛擬字節以確保相應的頁面被複制。
要同步,請丟棄現有的寫時複製視圖並創建一個新視圖。
我期望不必要的虛擬寫入的開銷可以忽略不計,但對齊塊以避免重疊頁面邊界可能不方便。
如果是這樣,方法B與方法A相同,除了有多個虛擬字節,間隔放置確保每個頁面重疊該塊包含至少一個虛擬字節。這增加了虛擬寫入的開銷,但我不希望它過度。但是,W每次需要進行更改時都會明確地進行這些虛擬寫入,例如,如果數據實際上不是按塊排列,或者如果每個塊內有多個虛擬字節,則可能會很尷尬不方便。因此,我們應該考慮方法Ç:
同時創建一個只讀和W上的讀寫視圖,以及在寫入時複製視圖R.包含W使用read - 僅查看讀取數據,但讀寫視圖寫入。
使用VirtualProtect和PAGE_GUARD來保護讀寫視圖中的所有頁面。
當防護頁錯誤被觸發時,讓異常處理程序在寫時複製視圖中的相應頁面上進行虛擬寫入。向量化的異常處理程序在我看來像最乾淨的選項。
注意:我的研究表明,儘管事實上這涉及到故意在頁面錯誤處理程序中調用頁面錯誤,但這並不是非常確切的說明。它應該得到支持,因爲任何異常處理程序都沒有合理的方法來確保它不引用被分頁的數據,但是由於我沒有找到明確的說明,因此建議進行一些實驗。
方法C是可能比阿效率較低或B,因爲它需要處理的額外頁錯誤異常,與相應的另外的往返行程到內核模式和背部。我也不確定涉及跟蹤防護頁面的頁面表開銷。但是,它可能更加方便,因爲消除來自處理代碼的虛擬寫入減少了代碼需要了解緩衝的程度。
最終變體避免了處理代碼通過使用單個視圖來知道緩衝的所有的需要,而不管W是讀還是寫。 方法d如下:
我相信這種方法效率最低,因爲我希望顯式更改塊的權限會比使用防護頁顯着慢。它也可能導致頁表更多碎片化。但是,如果事實證明它的性能足夠好,那幾乎肯定是最方便的解決方案。
一些其他注意事項:
我相信,所有這些方法應該工作,受到一個警告關於到底發生了什麼的時候,同時處理的第一個第二個頁面錯誤被觸發。對於不同變體的比較效率,我沒有那麼自信。做一些比較測試可能是明智的。
文件映射對象可能應該由頁面文件支持,並且您可能需要嘗試使用大頁面。這會增加需要複製的數據量,但會減少頁表上的負載。再次,比較測試可能是適當的。
我假設您已經考慮過這一點,但未來的讀者應該注意,根據數據的性質,根本不可能使用映射。例如:
的數據塊可以給予兩個修訂號,一個指示何時該塊變爲有效,而另一個時,它應該被認爲刪除。這種方法的時間開銷非常小,R只需要在處理塊時檢查版本號,以便它可以跳過太新的塊並刪除過時的塊。這涉及到較少的數據複製:W只需複製它正在處理的數據塊,而不是整個頁面,並且添加/刪除塊根本不需要複製任何數據。
如果塊需要按特定順序鏈接,修訂版號可能不夠,但您可以爲R和W分開鏈。同步將要求您重新鏈接塊,但這仍然可能比修改頁面表更快。
閱讀所有可以找到的文檔,所有對COW的引用都是在寫作的上下文中,通過編寫COW映射爲自己創建私有副本。我內心工作的思維模式是通過類似我的問題中的異常機制之類的東西,並且我一直假設,如果您要求爲一系列地址提供rw保護,這就是您真正獲得的地址,而不管存在其他映射,並且在那個世界中,由於寫入者的寫入沒有被捕獲,所以沒有辦法通過COW行爲來實現讀取器快照。我的假設錯了嗎? – moonshadow
AWE與VirtualProtect()結合使用可能會做到這一點。如果沒有,那可能是從設備驅動程序。但是,寫入時複製是否會涉及至少與原子交換和正確方法相同的內存複製?無論哪種方式,寫入的每個塊都被複制一次。 –
不,AWE將不起作用:「物理頁不能同時映射到多個虛擬地址。」 –