LWN: 20年後能否解決 negative dentry 問題!

語言: CN / TW / HK

關注了就能看到更多這麼棒的文章哦~

Negative dentries, 20 years later

By Jonathan Corbet April 11, 2022 DeepL assisted translation https://lwn.net/Articles/890025/ 

檔案系統和虛擬檔案系統層的任務就是管理實際存在的檔案,但 Linux 的 "dentry cache" (用來記住檔名查詢的結果),也同樣會跟蹤那些不存在的檔案。這個被稱為 "negative dentries" 的 cache 在系統的整體效能優化中起到了非常重要的作用,但如果這個 cache 增長得過大的話,它的本身功能就會帶來更多消極影響。隨著 2022 年 Linux Storage, Filesystem, and Memory-Management Summit(LSFMM)的臨近,"negative dentries" 的話題再次被提及;人們目前尚不清楚這次是否真的能解決這個問題。

核心裡的 dentry cache 儲存了在檔案系統中查詢一個檔案所得到的結果。如果下次需要再查詢同一個檔案就可以使用 cache 的結果,避免了再次走一遍底層檔案系統和訪問儲存裝置。重複查詢檔名的情況很常見,比如說 usr/bin/bash 或~ .nethackrc 都是例子,所以這是一個重要的優化。

至於為什麼要用 negative dentries 來記錄那些失敗的查詢,可能人們就不那麼容易一下子理解了。事實上經常出現反覆嘗試查詢一個不存在的檔案的情況,比如每次使用者輸入 "vi" 時,shell 都會遍歷 PATH 裡面的各個目錄(Emacs 使用者不會受影響因為他們啟動一次編輯器之後就再也不會離開這個舒服的環境)。更常見的情況是 program loader 尋找共享庫,或者編譯器尋找 include 檔案時產生的查詢失敗。在當今社會中,人們也經常收到建議說需要 "快速失敗(fail fast)",在查詢那些不存在的檔案時,這確實是個好建議。

因此,negative dentries 是個好東西,但正如我們都知道的,過猶不及。雖然正常 dentry 會受到實際存在的檔案數量的限制,但對不存在的檔案數量卻沒有什麼限制。因此,一個惡意(或根本不知道這裡門道的)應用程式很容易就會創造出大量的 negative dentry 。如果記憶體緊張,記憶體管理子系統最終會努力將其中一些 negative dentry 回收掉。但是,在沒有記憶體壓力的情況下這些 negative dentry 可以無限積累下去,記憶體總是會被耗盡的,那時就會留下一個大麻煩需要清理了。

有些核心問題很快就能解決,有些則需要更長時間。LWN 早在 2002 年就簡要地報道了關於 negative dentry 記憶體消耗的投訴了,差不多正好是 20 年前。在 2020 年初,LWN 也曾報道過一個最新的解決這個問題的嘗試。雖然隨著時間的推移,許多開發者已經對 negative dentry 問題進行過嘗試了,但核心問題仍然存在。這些 dentry 仍然在佔據寶貴的記憶體,而且它們還可能造成其他問題(如 soft lockup)。

A new discussion

在 3 月中旬 Matthew Wilcox 建議,negative-dentry 問題可以成為一個很好的 LSFMM 主題。"也許對這個問題進行一些集中的頭腦風暴可以引出一些真正的解決方案"。通常情況下,僅僅提出這樣一個話題就能引起為解決這個問題所需的頭腦風暴。這一次沒有發生,但它確實導致了 Stephen Brennan 釋出了一個 patch set,展示了一個解決這個問題的新方法。

negative-dentry 問題帶來的困難之一是很難知道什麼時候該開始回收。系統的規模和工作負載千變萬化,所以任何一種簡單的限制都有可能導致某個場景出現效能 regression。可以考慮為系統管理員提供一個調節這個閾值的開關,但是這只是把問題推給了使用者,人們普遍認為核心應該能夠自己解決這些問題。但是,正如 Brennan 指出的,這並不容易:

很難通過檢視一個 hash bucket 或 LRU list 來設計一個啟發式規則從而確定合理的 negative dentry 數量,因為這個方法無法從小型系統輕易擴充套件到大型系統上去。但是針對每個目錄來設定啟發式規則,就會更容易擴充套件,而且更容易推理。

這組 patch 提出的具體啟發式方法是,任何一個目錄的 negative dentry 數量不應超過 positive dentry 數量的五倍。如果一個目錄的 cache 中有 20 個 positive dentry,那麼 negative dentry 就不能超過 100 個。這是一個很好的想法,只是有一個小問題:核心並沒有針對每個目錄來統計相關的 dentries 的數量或型別進行統計。

為了解決這個問題,Brennan 添加了一些程式碼,在與每個目錄相關的 dentries list 中保持一個 "cursor"。每當有一個 dentry 操作(建立或刪除)發生時,這部分程式碼會在 list 中把 cursor 後移 6 個 dentry 位置;如果連一個 positive dentry 都沒有碰到過,那麼就可以認為已經超過了限制,從而進行一些 negative dentry 的清理。將這項工作安排到 dentry 操作本身裡面,就意味著這個開銷會由那些建立了大量 dentry 的程序本身來承擔,這似乎是個正確做法。

當然,這種方法的問題在於,沒有任何方法可以迫使 dentry 是以特定的順序新增到目錄的 list 中的。取決於建立 dentry 的順序,這種演算法可能會對 positive 和 negative dentry 的實際比例得出錯誤結論,從而做錯事。布倫南承認這個問題確實存在("確實這裡會對各種 workload 有不同結果,我承認這個問題"),但還沒有想出一個更好的主意。就目前的情況來看,這種演算法似乎肯定會導致一些極端案例,這可能會阻止人們接受這個 patch set,即使沒有其他什麼擔憂了。

The bigger problem

不過,在正常討論中,Dave Chinner 認為,對 negative dentry 的關注只是解決了問題的表象,而忽略了更大的問題。他說,真正的問題是,記憶體壓力是核心控制它所維護的許多 cache 的大小的唯一決定因素:

是的,這裡的根本問題是,當 沒有記憶體壓力 的時候,記憶體回收機制對管理這些長期積累下來的單一使用目的的 cached object (快取物件)毫無幫助。有[大量]的空閒時間和閒置資源可以合理地用來管理 cache,但我們沒有。例如,沒有對 cache 進行定期 rotate 從而觸發對這些一次性使用的物件的檢測和回收(例如在使用之後幾分鐘內進行檢查),從而防止它們不必要地佔滿所有的記憶體,並在記憶體最終被用光時產生短暫的記憶體回收和分配延遲的高峰。

他說,與其擔心 dentry cache,開發者不如想出一種機制來管理所有核心內 cache 的大小。Wilcox 原則上同意,但警告說不要把問題搞得太廣泛,以至於變得難以解決。Chinner 則重申了這一點,他說有多個核心 cache 都有同樣的問題,而基於某種定期掃描以使 cache 內的內容老化的解決方案,可以立即適用於所有的這些型別的 cache 中。

這個討論已經結束了,沒有人提出要建立 Chinner 希望看到的更通用的緩衝區老化機制。但問題仍然存在,而且似乎不太可能自己消失。所以這個討論出現在 LSFMM 檔期的可能性似乎相當高。也許三年來第一次在記憶體管理和檔案系統領域的討論將導致對解決方案達成某種程度的共識,最好是在已經過去了 20 年後的今天就能實現。

全文完

LWN 文章遵循 CC BY-SA 4.0 許可協議。

歡迎分享、轉載及基於現有協議再創作~

長按下面二維碼關注,關注 LWN 深度文章以及開源社群的各種新近言論~