持久內存編程

持久內存編程
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,使用手冊可從http://pmem.io查看。
libpmem:基本庫
這個庫比較小,相對簡單,包含探測CPU支持哪種刷寫指令以及使用最佳指令進行範圍拷貝。
libpmemobj:支持事務
Libpmemblk和libpmemlog:支持特定用戶案例
Libmemkind:持久內存易失性使用
總結
2013年的ideas成熟了並加到了完整的編程模型中。Pmdk庫被開發出來,以供持久內存編程使用。該庫在GitHub上開源https://github.com/pmem
原文
https://www.snia.org/pm-summit2019
https://www.snia.org/pm-summit2017

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章