2012-01-02 104 views
7

我需要一個非複製的非常大的mmap文件的重新大小,同時仍允許併發訪問讀者線程。快速調整mmap文件的大小

簡單的方法是在相同的文件上使用兩個MAP_SHARED映射(增長文件,然後創建包含增長區域的第二個映射),然後取消映射舊映射,一旦所有可以訪問它的讀取器完成。但是,我很好奇下面的方案是否可行,如果是的話,它是否有優勢。

  1. MMAP與MAP_PRIVATE
  2. 文件
  3. 做只讀在多線程
  4. 要麼獲得該文件的互斥這個內存訪問,寫入內存(假設這在某種程度上將完成讀取器可能讀取的內存不會被它搞砸)
  5. 或獲取互斥鎖,但增加文件的大小並使用mremap將其移動到新地址(調整映射的大小而不復制或不必要的文件IO)

瘋狂的部分進來(4)。如果移動內存,則舊地址將變爲無效,而仍在讀取它的讀者可能突然出現訪問衝突。如果我們修改讀取器來捕獲這個訪問衝突,然後重新開始操作(即不重新讀取錯誤的地址,重新計算給定偏移量的地址和mremap中的新基址)。是的,我知道這是邪惡的,但在我看來,讀者只能成功讀取舊地址處的數據,或者因訪問衝突而重試失敗。如果足夠小心,那應該是是安全的。由於重新調整大小不會經常發生,讀者最終會成功,而不會陷入重試循環。

如果舊地址空間重新使用,而閱讀器仍然有指向它的指針,則可能會出現問題。然後將不會有訪問衝突,但數據將不正確,程序進入未定義行爲的獨角獸和糖果填充的土地(其中通常既不是獨角獸也不是糖果)。

但是,如果您完全控制了分配並且可以確保在此期間發生的任何分配都不會重複使用舊的地址空間,那麼這應該不成問題,行爲不應該是未定義的。

我對不對?這可以工作嗎?使用兩個MAP_SHARED映射有沒有什麼好處?

+0

您可以只使用讀寫鎖定並保護寫入鎖定下的重新映射,沒有? – fge 2012-01-02 18:01:56

+0

我猜測瓶頸是磁盤。你確定這是值得的麻煩嗎?從磁盤複製大文件總是需要時間,因爲磁盤是慢速的機械設備。 – 2012-01-02 18:03:03

+0

fge,是的,但在這種情況下鎖定讀線程是不可能的 – Eloff 2012-01-02 18:07:17

回答

4

對於我來說,很難想象一個你不知道文件大小的上限的情況。假設這是真的,您可以通過在mmap()中首次映射文件時提供該大小來「保留」地址空間以獲得文件的最大大小。當然,超出文件實際大小的任何訪問都會導致訪問衝突,但這就是您希望它無論如何都能正常工作的原因 - 您可能會認爲保留額外地址空間可確保訪問衝突,而不是保留該地址範圍開放給被mmap()或malloc()等其他調用使用。

不管怎樣,關鍵是我的解決方案,你永遠不移動的地址範圍,只改變它的大小,現在你的鎖是圍繞着數據結構,提供了當前有效的大小給每個線程。

如果您的文件太多,以致每個文件的最大映射數量超出地址空間,但我的解決方案無法工作,但這是64位地址空間的年限,因此希望您的最大映射大小爲「問題。我只寫了一個小程序來說服自己,當你嘗試訪問超出文件大小的文件時,創建大於文件大小的映射會導致訪問衝突,然後在ftruncate()文件變得更大時工作正常,全部使用從第一個mmap()調用返回的相同地址。)

+0

這是我的第一個想法,但令人遺憾的是,在現代操作系統中,地址空間限制在8TB,這意味着如果有大量內存映射文件都預留了最大可能空間,則可能會耗盡地址空間。可以使用mmap和MAP_NORESERVE在程序啓動時儘可能多地保留地址空間,然後使用MAP_FIXED分配地址空間,首先均勻地分配地址空間,然後從耗盡的地圖中分配最少的地圖。然後使用上述策略或標準memcpy,如果mmap空間不足並且沒有保留的相鄰空間。 – Eloff 2012-01-02 19:25:10

+0

你從哪裏得到你的8TB數字? – camelccc 2012-11-29 16:05:12

+2

Linux可以在128TB地址空間內管理64TB的phsyiscal memory。不知道你是否有大量的文件大於128TB(即使我的網絡共享上的完整RAID卷僅僅是24TB,但是你的里程可能會有所不同)。你不是碰巧是Google,是嗎? – Damon 2014-06-22 14:23:54

相關問題