2014-06-23 26 views
3

有沒有一種方法可以通過MAP_PRIVATE創建寫時複製映射,寫入一些數據(即弄髒某些頁面),然後放棄我的更改,沒有使用munmap並重新映射?目標是爲給定的映射保留相同的虛擬地址(如果我取消映射&然後再次映射相同的文件,則不保證會發生),但是一次性丟棄所有COW更改。丟棄髒寫時複製頁面

我的理解是,試圖通過提示地址來重新映射空間並使用MAP_FIXED標誌可能會產生這種效果;但是我不確定我對MAP_FIXED文檔的解釋是否正確,或者如果此行爲得到保證。

要從MMAP引用(2)文檔:

If the memory region specified by addr and len overlaps pages of any existing 
mapping(s), then the overlapped part of the existing mapping(s) will be 
discarded. 

請問在這種情況下,「放棄」意味着任何COW頁面就會被扔掉,並從相應的頁面新的讀取將發生故障,反映更改在磁盤上?

回答

3

如果您執行與現有映射重疊的mmap操作,則Linux內核將搜索現有映射的重疊部分,就像先完成了unmap一樣。因此,例如,如果您映射一個共享庫曾經存在的幀緩衝區,那麼該內存現在與共享庫無關;它指向幀緩衝區。

已移除映射中的底層頁面對象與映射無關:頁面是引用計數的對象。當兩張地圖共享相同頁面的視圖時,僅僅是由於在不同視圖中「安裝」了相同的頁面。當頁面變髒,然後未映射時,這不會創建依賴關係,從而必須在新映射之前寫出髒頁面;在原始髒頁面(例如,文件支持的共享映射的一部分)被刷新之前,虛擬內存可能已被重新分配給新映射(例如一塊圖形幀緩衝區)。

關於扔掉映射:我不認爲你可以做到這一點。也就是說,如果你有一個應該將髒頁面刷新到底層文件的映射,你就不能寫入那個內存,然後很快(或者mmap某種東西)寫入,希望永遠不會寫入。在Linux的madvise API中,似乎有相關的MAP_REMOVE操作,但根據手冊頁,它似乎只能在tmpfsshmfs上運行。我認爲阻止寫作發生的唯一方法就是進行稱爲「潛水電源開關」的歷史悠久的儀式。

有一種方法可以映射文件對象,以便不會傳播更改:即MAP_PRIVATE(與MAP_SHARED相反)。例如,需要MAP_PRIVATE的調試器,如gdb,它們需要能夠將斷點放入可執行文件或共享庫中,而不必在每個正在運行的進程中向該可執行文件或庫的每個實例發送陷阱指令(以及磁盤上的副本!)。

如果你有一個MAP_PRIVATE與修改的部分,並且你取消映射它(或那些部分)或映射其他頂部的東西,我相信它們將被丟棄。這些頁面應該已經進行了寫入複製,因此使它們變髒的過程應該保留唯一的引用。當它們未被映射時,它們的引用次數下降到零,並且因爲它們是私人頁面,所以它們會變成草皮。

+0

非常有趣。因此,如果我1)在一個文件上創建一個MAP_PRIVATE映射,2)將它讀入頁面緩存,3)通過寫入該私有的COW映射,玷污一些頁面,4)用MAP_FIXED標誌重新映射mmap,覆蓋地址MAP_PRIVATE映射的空間,我應該保留底層文件的「真實」數據在頁面緩存中,以及拋棄我對COW「更改」的看法。這是非常方便的行爲。 – Bryce

+0

布賴斯,是的;我相信文件中的原始數據應該重新出現;如果您要「查看」這些位置和「讀取」,您會看到相同的數據。 – Kaz

1

即使在複製之後,虛擬地址仍保持不變。只有物理地址發生變化(和相關的存儲器映射頁寄存器)。

該進程寫入頁面後,撤消它已爲時過晚。複製發生在首次寫入內存區域期間。

+0

我知道我實際上會在第一個副本上寫入物理RAM,但我不能「撤消」它 - 但問題是具有重疊地址(或某種其他機制)的新mmap是否會導致相同從映射過程的角度來看,虛擬內存以一種拋棄這些變化的方式被「重新初始化」。 – Bryce