本文根據 https://lwn.net/Articles/712467/
翻譯改編而來。
随着3DX POINT相關技術的逐漸普及和推廣,我們可以預見很快持久化記憶體(Persistent Memory)将會被使用得越來越多,同時也催生了Linux核心很多方面的革新,Matthew Wilcox的DAX就是其中非常著名的一個,這個東東可以讓使用者對于基于持久化記憶體的檔案系統跳過頁面緩存(Page Cache)直接通路檔案系統,而Dave Chinner甚至預測有了DAX,我們将不再需要頁面緩存了。這個從1995年開始存在于Linux核心并造福無數蒼生的玩意難道要壽終正寝了麼?作為DAX的始作俑者,Matthew Wilcox又是怎麼認為的?讓我們一起去聽聽他在今年linux.conf.au上的演講。
緩存的重要性
首先計算機就是緩存的世界,君不見體系結構領域裡面關于cache相關的paper一直是層出不窮,此起彼伏。Wilcox甚至回過頭查閱了1975年發行的Unix第六版,并在那裡找到了使用緩沖區緩存的例子。同時Wilcox舉例說隻要緩存都命中,他的新電腦每秒可以執行100億條指令。但是記憶體每秒隻能跑5億3千萬條cache line,是以緩存未命中就會嚴重影響性能。如果資料沒有緩存到主存,需要從儲存設備讀, 即使是快速的SSD,也會變得很慢。 PDP-11會因緩存未命中而顯著變慢,幾十年過去了,這個問題反而惡化了。因為CPU的發展速度比記憶體快,而同時記憶體的發展速度相比存儲也更快,是以緩存未命中的帶來的性能損失也會越來越嚴重。
什麼是Linux的頁面緩存
如前文所述,很久以來,Unix系統都有緩沖區緩存(Buffer Cache),位于檔案系統與磁盤之間,目的是為了緩存磁盤塊到記憶體中。Linux從一開始就有個緩沖區緩存。在1995年發行的1.3.50版本中,Linus Torvald做了一個重大創新——頁面緩存。頁面緩存與緩沖區緩存的差別在于,它是位于虛拟檔案系統(VFS)與檔案系統本身之間。有了頁面緩存,如果所需的頁面已經存在,則根本無需調用檔案系統的代碼。起初,頁面緩存和緩沖區緩存是完全獨立的,在1999年,Ingo Molnar統一了它們。現在,緩沖區緩存仍然存在,但是内容是指向頁面緩存。
頁面緩存有許多非常重要的功能,比如可以通過給定的索引查找頁面。如果頁面不存在,則建立并從磁盤填充相應的内容。髒頁可以刷回磁盤,頁面可以被鎖定,解鎖,以及從緩存中删除。線程可以等待頁面狀态的變化,也可以通過接口對給定狀态的頁面進行搜尋,頁面緩存還能夠追蹤與外部存儲相關的錯誤等等。
另外頁面緩存還需要有一套手段來對未來的使用進行預測。當緩存增長太大時,各種啟發算法開始決策哪些頁面應當被移除。僅使用了一次的頁面很可能不會被再使用,是以它們将保留在“不活躍”連結清單(inactive list)并相對較快的換出。第二次使用将會把頁面從不活躍連結清單轉移到活躍連結清單(active list),活躍連結清單的頁也會因為逾時而被移到不活躍清單。 有一個例外,“影子”條目用于追蹤已脫離不活躍連結清單并已回收的頁面,這些條目可以延長相對遙遠的過去使用過的頁的生命周期。
随着記憶體變大,大頁和透明大頁也開始普及起來。不過透明大頁(THP)特性最初隻能用于匿名(非檔案後端)記憶體,把大頁在頁面緩存中使用同樣也會有很多優點。不過由于radix tree的限制,将大頁作為頁面緩存是Linux核心的一個挑戰。最早的一個嘗試是簡單地往頁面緩存中增加了大量單頁條目,以對應于單個大頁。Wilcox認為這種方法是“愚蠢的”,他增強了用于追蹤頁面緩存中頁面的radix tree代碼,使得頁面緩存可以使用單個條目來表示對應的大頁,進而使radix tree能夠直接處理大頁的條目。
是否仍然需要頁面緩存?
介紹完頁面緩存光輝的曆史和強大的功能以後,我們回到開頭的問題,現在Linux核心是否還需要頁面緩存呢?由于Wilcox的DAX實作,Dave Chinner預測我們将不再需要頁面緩存。當然同樣也有其他人不同意Chinner,Linus Torvalds也是其中之一。Torvalds在一個單獨的論壇中指出頁面緩存很重要,因為在資料通路的關鍵路徑上,好的東西從來不是出自低層的檔案系統代碼(譯者作為一個檔案系統開發者,表示很受傷,附上文中Torvalds的原話:the page cache is important because good things don’t come from having low-level filesystem code in the critical path for data access)。
對于是否需要頁面緩存,DAX的作者Wilcox是咋想的呢?他說“沒有什麼比你的同僚懷疑你的整個動機更糟糕的了”,是以貌似他也不是想幹掉頁面緩存。但是等等,為啥Dave Chinner會得出與之相反的結論呢?
原來Wilcox在設計他的DAX代碼的時候,其實真的沒有使用頁面緩存。當一個應用使用類似于read()的系統調用從存儲在持久化記憶體中的檔案中讀取資料時,DAX會介入。由于請求的資料不存在于頁面緩存中,VFS層調用檔案系統特定的read_iter()函數。這反過來調用到DAX代碼,它将回調到檔案系統将檔案偏移轉換為塊号,然後查詢塊層以擷取持久化記憶體塊的位置(如果需要,将其映射到核心位址空間),最終使得塊内容可以被拷貝回應用。
但是現在Wilcox覺得當初他的這個設計是錯誤的,read()應該以另外一種方式工作,初始的步驟是相同的,因為read_iter()函數仍将被調用,同時它将調用到DAX代碼。但是,DAX不是回調到檔案系統,而是應當調用到頁面緩存以擷取檔案中所需偏移關聯的實體位址,然後資料從該位址拷貝到使用者空間。這樣的邏輯就和現在頁面緩存的工作原理完全一緻了,而且如果資訊已經存在頁面緩存中的情況下,低層檔案系統的代碼完全無需介入。
Wilcox在這裡又再次引用了Torvalds關于頁面緩存的文章:
從鎖的角度來看,這也是一個重大的災難:相信我,如果你認為你的檔案系統可以進行細粒度的鎖,當諸如并發路徑查找的事情到來時,你會生活在一個夢幻世界。
Torvalds先生的話是“如此正确”,Wilcox說。DAX中的鎖實作的确是災難性的。他最初認為可能用相對簡單的鎖來解決,但複雜性在每個新發現的邊緣場景蔓延。DAX的鎖實作現在“實在醜陋”,他很抱歉他犯了一個錯誤,認為可以繞過頁面緩存。現在,他說他必須去彌補這個錯誤。
未來的工作
Wilcox同時總結了圍繞DAX和頁面緩存的一系列增強。前文提到的大頁支援優化是其中之一,這已經在mm樹中,應該很快就可以完成。使用頁框數而不是頁面也已經讨論了一段時間,因為讓核心為這些大型持久化記憶體保持大量的頁面結構體(struct page)是完全沒有必要的。
另外他想重新考慮檔案系統塊尺寸大于系統頁面尺寸的想法,這是人們多年想要的東西。現在既然頁面緩存可以處理多個頁面大小,這個想法應該已經有希望了。不過他自己沒時間折騰這個,是以他正在找尋其他感興趣的開發人員一起來做這個項目。
交換大頁也是一個不錯的領域。我們已經在記憶體中使用了大頁,但當這些大頁被換出時,他們又被分解成普通尺寸的頁面。針對這一問題,目前已經有一些提升交換性能的工作在進行,但是這個解法從根上來看可能就是不對的,正确的解法應該是讓交換出去的大頁保持在一起。相應的,這同樣也有助于我們将記憶體交換到持久化記憶體。因為使用持久化記憶體的交換空間中的資料仍然可以被通路,是以将其留在那可能是有意義的,尤其是當資料沒有被大量修改的時候。
最後附上Wilcox的演講視訊,視訊中還包含了頁面緩存鎖的相關分析和讨論。
結論
是以貌似即使我們有了持久化記憶體,頁面緩存還會繼續存在下去。但是譯者覺得随着持久化記憶體速度越來越快,價格越來越便宜,也許核心社群會有一些新的想法也說不定,讓我們拭目以待吧。