持久記憶體程式設計
持久記憶體程式設計
2013 年 6 月我寫了關於非易失性記憶體( NVM )的未來介面。其中描述了 SNIA NVM Programming technical work group ( TWG )正在開發的 NVM 程式設計模型。在過去的四年裡,規範已經發布,正如預測的那樣,程式設計模型已成為大量後續工作的重點。該程式設計模型,在規範中描述為 NVM.PM.FILE ,可以將 PM 當做檔案被作業系統對映到記憶體。本文,介紹持久記憶體程式設計模型如何在作業系統中實現,已經做了哪些工作,以及我們還面臨著哪些挑戰。
持久記憶體背景
PM 和 storage class memory 是同一的術語,具有位元組定址、載入 / 儲存記憶體訪問特性,但具備永續性。本文,關注將 PM 掛在系統記憶體匯流排上,例如 DRAM DIMM ,建立一類稱為 NVDIMMs 的非易失 DIMMs 。
為進一步闡述所說的持久記憶體是什麼,僅討論NVDIMMs ,允許軟體像訪問記憶體一樣訪問。提供了記憶體語義的所有優點,例如 CPU CACHE 一致性、其他裝置直接記憶體訪問 DMA 、緩衝線粒度訪問,即可位元組定址。為提供這些語義,裝置必須足夠塊以便指令訪問 CPU 時拖延 CPU 合情合理。 NAND flash 當做持久記憶體時比較慢,因為需要以塊為單位進行訪問,並需要足夠長的時間進行上下文切換。硬體訪問的時間通常以毫秒為單位, NAND Flash SSD 以微妙為單位, PM 以納秒為單位。依賴於硬體媒體型別, NVDIMM 可能速度還比不上 DRAM ,但是速度已經堪比他的速度了。
現在市場上的一些NVDIMM 產品,執行時使用 DRAM 作為媒介,斷電時自動將內容備份到 NAND Flash ,再次上電時將 NAND Flash 內容返回 DRAM 。這些產品提供 DRAM 的效能,但是需要額外的部件和電池來儲存資料,相比 DRAM ,為每個 DIMM 提供小容量、每 G 高消耗的特性。新出現的非易失性媒介,例如 2015 年, Intel 和美光聯合研發的 3D XPoint 技術,透過比 DRAM 更高的容量。每個 CPU 達到上 T 的頻寬,使持久記憶體引起多方前沿關注:永續性、容量、消耗。
持久記憶體程式設計模型
如何是應用訪問持久記憶體?和易失性記憶體不同,應用需要特定方法和指定的持久內容連線;持久記憶體不像易失性記憶體一樣是匿名的,他需要像檔案一樣命名一個區域,這樣應用才能找到他。應用需要具有訪問持久記憶體的控制許可權。推薦SNIA TWG 程式設計模型是因為作業系統可以使用標準的檔案語義提供持久記憶體的命名、許可權和記憶體對映。
當前,多種作業系統包括Linux 和 Windows 都支援了這種模型。
DAX
Figure 1 中顯示的持久記憶體適配的檔案系統,允許直接訪問持久記憶體,而不經過系統的 page cache 。這樣的特性稱為 DAX 。持久記憶體程式設計模型和 DAX 特性表明持久記憶體檔案可以使用 mmap() 或 MapViewOfFile() 類似的標誌函式對映到記憶體。這種型別時圖 1 最右邊形式。應用直接透過 load/store 指令訪問持久記憶體。允許直接訪問持久媒介而不用進行使用者態和核心態的切換。
儲存持久化
Linux 系統可以使用 msync() 或 fsync() 確保資料持久化, Windows 可以透過 FlushViewOfFile() 和 FlushFileBuffers() 確保資料持久化。這些呼叫會建立一個記憶體柵,這個點之前的資料都已經全部持久化到持久記憶體。歷史上,這個儲存柵需要作業系統找到 page cache 中的髒頁,然後將他們刷寫到磁碟。由於持久記憶體不使用 page cache ,作業系統僅需要將 CPU cache 中的變動刷寫到持久記憶體。如圖 2 所示:
圖2 中虛線部分顯示了持久域。這種級別的架構,虛線部分的資料要麼在 DIMM ,要麼咋記憶體控制器的寫請求佇列 WPQ 。無論哪種返回,持久記憶體需要有足夠的電量將虛線框中的資料刷寫到持久媒介。這種特性叫做非同步 DRAM 刷,並 NVDIMM 已經具備這種特性。
X86 架構中,簡單執行儲存指令並不能確保資料持久化,因為資料可能仍然在 CPU cache ,一旦斷電,這些資料就會丟失。需要額外的刷寫指令確保資料持久化。下表描述了他們如何工作。
圖2 和表 1 中可能會使人迷惑,為什麼 Intel 不將 CPU cache 弄到持久域部分。技術上可行,圖 2 中的虛線框內包括 CPU CACHEs 。
擴充套件持久域包括CPU CACHEs 的問題是 x86 的 caches 非常大,他需要的電量比電容器實際能提供的電量多的多。這就意味著平臺需要電池。此時支援持久記憶體的伺服器都配一個電池不太現實。但是對於硬體供應商來說,當然有可能在其商品中包含一個電池。這就允許跳過表 1 中描述讀緩衝重新整理指令,但是 sfence 指令仍然是必須的,因為儲存屏障儲存只有在全域性可見時才被認為是持久的,這就是 sfence 確保的。
因為應用供應商計劃使用電池以及未來期望所有平臺都將CPU cache 包含到持久域,所以在 ACPI 中新增一個屬性,這樣當跳過 CPU 刷時, BIOS 可以通知作業系統。這就允許作業系統以最優的方式實現類似 msync 的呼叫。
刷寫使用者空間到持久域
WBINVD 例外, Intel CPU 以使用者態模式支援表 1 中描述的指令。使用 CLWB ( CLFLUSHOT 或 CLFLUSH )刷寫 cache line 並支援用使用者態使用臨時儲存。
這就允許從使用者空間刷寫到持久記憶體,而不需要經過核心,這個特性稱為Optimized Flush 。依賴於作業系統和硬體,各個平臺選擇性支援這個特性。儘管有 CPU 支援,但是對於應用程式來說只有作業系統說安全時才使用 optimized flush 。當檔案系統後設資料改變需要 msync 刷寫時,作業系統需要這個控制點。
當前實現中支援安全的使用者空間刷正不斷演化。Windows 的 DAX 由 NTFS 檔案系統提供,包括無條件支援 Optimized flush 。 Windows 使用類似 CLWB+SFENCE 的指令保持資料持久化到持久記憶體。 Linux 中的 ext4 和 xfs 支援 DAX ,不需要考慮使用者空間刷寫安全性。作為臨時解決方案, Linux 提供 Device-DAx ,允許應用開啟持久記憶體裝置,將其對映到記憶體,利用使用者空間刷寫確保永續性。
Libpmem 庫提供函式告訴應用程式何時 Optimized flush 是安全的。強烈建議程式設計師使用 libpmem 來確定並使用使用者空間刷寫。 Libpmem 也被用來檢測平臺使用電池的情況,將刷寫呼叫轉換成簡單的 sfence 指令呼叫。下文會詳細介紹這個庫。
持久記憶體挑戰
記憶體中資料結構改變時原子性問題就出現了。其他執行緒訪問這個資料結構時會不會僅考到修改到一半的資料?多執行緒程式設計時通常使用鎖來保護資料結構。有時也會使用指令確保硬體中的原子性。本文中原子性也成為可見性,當修改提交時,另外一個執行緒才能看到這個執行緒的修改。
Libpmemobj 庫提供事務保證,確保斷電安全。在持久記憶體出現前,斷電等中斷寫時,記憶體狀態不會出現問題,因為是易失的。但是持久記憶體中,需要理解部分狀態刷後就已經持久化。 Intel 僅使用 8 位元組儲存確保故障原子性。大於 8 位元組的將不保證資料一致性。
其他挑戰:管理空間。因為持久記憶體域被當做檔案,檔案系統可以管理這個空間,但是一旦被應用對映到記憶體,檔案中發生的事情完全取決於應用。和malloc 類似的函式分配的記憶體是易失的,在重啟時不提供方法重連持久記憶體對,也不辭去任何步驟保證出現故障時資料一致性。所以持久記憶體程式設計中也需要著重處理空間分配問題。
地址獨立性是另一個挑戰。儘管技術上可以實現持久內對映到同一個地址上,但是當其他對映items 大小改變時,這就不切實際了。一種地址空間佈局隨機化的特性會使作業系統隨機調整庫和檔案對映地址。地址獨立意味著持久記憶體中資料結構引用另一個使用指標的資料結構,即使檔案對映到不同地址,這個指標也必須以某種方式使用。有幾種方法實現這一點,例如在對映後重新定位指標,使用相對指標而不是絕對指標,或者使用某種型別的物件 ID 來應用駐存在持久記憶體中的資料結構。
NVM庫
Intel 開發的 pmdk 庫,在 GitHub 上開源,開源協議 BSD ,使用手冊可從 檢視。
libpmem :基本庫
這個庫比較小,相對簡單,包含探測CPU 支援哪種刷寫指令以及使用最佳指令進行範圍複製。
libpmemobj :支援事務
Libpmemblk 和 libpmemlog :支援特定使用者案例
Libmemkind :持久記憶體易失性使用
總結
2013 年的 ideas 成熟了並加到了完整的程式設計模型中。 Pmdk 庫被開發出來,以供持久記憶體程式設計使用。該庫在 GitHub 上開源
原文
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31493717/viewspace-2684583/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Rust 程式設計:記憶體佈局Rust程式設計記憶體
- 《Windows核心程式設計》---又是記憶體Windows程式設計記憶體
- windows核心程式設計--記憶體堆疊Windows程式設計記憶體
- 程式設計師對記憶體的理解程式設計師記憶體
- java優化程式設計-記憶體管理Java優化程式設計記憶體
- Redis持久化——記憶體快照(RDB)Redis持久化記憶體
- 持久記憶體指令(PMDK)簡介記憶體
- 記憶體池設計記憶體
- 高階記憶體管理程式設計指南-實用的記憶管理記憶體程式設計
- Java併發程式設計:Java記憶體模型Java程式設計記憶體模型
- 記憶體和磁碟設計記憶體
- windows核心程式設計--記憶體對映檔案Windows程式設計記憶體
- Java併發程式設計之Java記憶體模型Java程式設計記憶體模型
- 關於持久記憶體(PMem)你知道多少?記憶體
- 面試必問併發程式設計記憶體模型JMM與記憶體屏障剖析 學習面試程式設計記憶體模型
- Java程式設計技術之淺析JVM記憶體Java程式設計JVM記憶體
- Linux系統程式設計—共享記憶體之mmapLinux程式設計記憶體
- 《java併發程式設計的藝術》記憶體模型Java程式設計記憶體模型
- 【程式設計師的自我修養①】iOS記憶體管理程式設計師iOS記憶體
- 淺析C#程式設計中的記憶體管理C#程式設計記憶體
- 針對持久記憶體的後寫日誌記憶體
- RabbitMQ持久化機制、記憶體磁碟控制(四)MQ持久化記憶體
- 計算程式記憶體需求最小量記憶體
- LINUX系統程式設計 LINUX 虛擬記憶體Linux程式設計記憶體
- Redis基礎篇(四)持久化:記憶體快照(RDB)Redis持久化記憶體
- 持久記憶體-RDMA讓遠端資料不再遠記憶體
- Redis系列:RDB記憶體快照提供持久化能力Redis記憶體持久化
- Objective-C高階程式設計讀書筆記之記憶體管理Object程式設計筆記記憶體
- [譯] C程式設計師該知道的記憶體知識 (1)程式設計師記憶體
- [譯] C程式設計師該知道的記憶體知識 (4)程式設計師記憶體
- 程式設計師需要了解的硬核知識之記憶體程式設計師記憶體
- Linux 程式設計1:深入淺出 Linux 共享記憶體Linux程式設計記憶體
- Linux系統程式設計之命名管道與共享記憶體Linux程式設計記憶體
- 深入理解併發程式設計藝術之計算機記憶體模型程式設計計算機記憶體模型
- 如何設計一個記憶體分配器?記憶體
- 程式設計師修仙之路--突破記憶體限制的高效能排序程式設計師記憶體排序
- AIX程式記憶體佔用數的計算AI記憶體
- 記憶體的計算記憶體