開發與維運

我們還需要Page Cache麼?

本文根據 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的演講視頻,視頻中還包含了頁面緩存鎖的相關分析和討論。

結論

所以貌似即使我們有了持久化內存,頁面緩存還會繼續存在下去。但是譯者覺得隨著持久化內存速度越來越快,價格越來越便宜,也許內核社區會有一些新的想法也說不定,讓我們拭目以待吧。

Leave a Reply

Your email address will not be published. Required fields are marked *