SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

OpenAnolis小助手發表於2022-07-07

文/系統運維 SIG

在《AK47 所向披靡,記憶體洩漏一網打盡》一文中,我們分享了slab 記憶體洩漏的排查方式和工具,這次我們分享一種更加隱祕且更難排查的"記憶體洩漏"案例。

一、 問題現象

客戶收到系統告警,K8S 叢集某些節點 used 記憶體持續升高,top 檢視程式使用的記憶體並不多,剩餘記憶體不足卻找不到記憶體的使用者,記憶體神祕消失,需要排查記憶體去哪兒了。

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

執行 top 指令並按記憶體排序輸出,記憶體使用最多的程式才 800M 左右,加起來遠達不到 used 9G 的使用量。

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

二、問題分析

2.1 記憶體去哪兒了?

在分析具體問題前,我們先把系統記憶體分類,便於找到記憶體使用異常的地方,從記憶體使用性質上,可以簡單把記憶體分為應用記憶體和核心記憶體,兩種記憶體使用量加上空閒記憶體,應該接近於 memory total,這樣區分能夠快速定位問題的邊界。

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

其中 allocpage 指通過 __get_free_pages/alloc_pages 等 API 介面直接從夥伴系統申請的記憶體量(不包含 slab 和 vmalloc)。

2.1.1 記憶體分析

根據記憶體大圖分別計算應用記憶體和核心記憶體,就可以知道是哪部分存在異常,但這些指標計算比較繁瑣,很多記憶體值還存在重疊。針對這個痛點,SysOM 運維平臺的記憶體大盤功能以視覺化的方式展示記憶體的使用情況,並直接給出記憶體是否存在洩漏,本案例中,使用 SysOM 檢測,直接顯示 allocpage 存在洩漏,使用量接近 6G。

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

2.1.2 allocpage 記憶體

那既然是 alloc page 型別的記憶體佔用多,是否可以直接從 sysfs、procfs 檔案節點檢視其記憶體使用了?很遺憾,這部分記憶體是核心/驅動直接呼叫 __get_free_page/alloc_pages 等函式從夥伴系統申請單個或多個連續的頁面,系統層面沒有介面查詢這部分記憶體使用詳情。如果這類記憶體存在洩漏,就會出現"記憶體憑空消失"的現象,比較難發現,問題原因也難排查。針對這個難點,我們的SysOM 系統運維能夠覆蓋這類記憶體統計和原因診斷。

所以需要進一步通過 SysOM 的診斷利器 SysAK 動態抓取這類記憶體的使用情況。

2.2 allocPage 型別記憶體排查

2.2.1 動態診斷

對於核心記憶體洩漏,我們直接可以使用 SysAK 工具來動態追蹤,啟動命令並等待 10 分鐘。

sysak memleak -t page -i 600

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

診斷結果顯示 10 分鐘內 receive_mergeable 函式分配的記憶體有 4919 次沒有釋放,記憶體大小在 300M 左右,分析到這裡,我們就需要結合程式碼來確認 receive_mergeable 函式的記憶體分配和釋放邏輯是否正確。

2.2.2 分配和釋放總結

1)page_to_skb 每次會分配一個線性資料區為 128 Byte 的 skb。

2)資料區呼叫 alloc_pages_node 函式,一次性從夥伴系統申請 32k 記憶體(order=3)。

3)每個 skb 會對 32k 的 head page 產生一次引用計數,也就是隻有當所有 skb 都釋放時,這 32k 記憶體才釋放回夥伴系統。

4)receive_mergeable 函式負責申請記憶體,但不負責釋放這部分記憶體,只有當應用從 socket recvQ 中把資料讀走才會對 head page 引用計數減一,當 page refs 為 0 時,釋放回夥伴系統。

當應用消費資料比較慢,可能會導致 receive_mergeable 函式申請的記憶體釋放不及時,而且最壞情況一個 skb 會佔用 32k 記憶體,使用 sysak skcheck 檢查 socket 接收佇列和傳送佇列殘留情況。

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

從輸出可以知道,系統中只有 nginx 程式的接收佇列有殘留資料,socket fd=11 的 Recv-Q 有接近 3M 的資料沒有接收,通過直接 kill 146935,系統記憶體恢復正常了,所以問題根本原因就是 nginx 沒有及時收走資料了。

三、問題結論

經過與業務方溝通,最終確認是業務配置問題,導致 nginx 有一個執行緒沒有處理資料,從而導致網路卡驅動申請的記憶體沒有及時釋放,而 allocpage 記憶體又是無法統計的,從而出現記憶體憑空消失的現象。

結論驗證

接收佇列真的有資料殘留嗎,這裡結合 crash 工具的 files 指令通過 fd 找到對應的sock:

socket = file->private_data
sock = socket->sk

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

通過多次觀察,發現 sk_receive_queue 上的 skb 長時間沒有變化,這也證明了 nginx 沒有及時處理接收佇列上的 skb,導致在網路卡驅動中分配的記憶體沒有釋放。

四、記憶體洩漏疑點

在排查過程還遇到一個非常較困惑的地方,sockstat 和 slabtop 看檢查 tcp mem 和 skbuff_head_cache 使用都很正常,導致進一步掩蓋了網路佔用的記憶體。

tcp mem = 32204*4K=125M

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

skb 數量在 1.5萬~3 萬之間。

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

按照前面分析,一個skb最壞情況佔用 32k 記憶體,那麼 2 萬個 skb 最大也就佔 600M 左右,怎麼會佔用幾個 G 了,難道分析有問題?如下圖所示,skb 的非線性區可能還存在若干個 frag page,而每個 frag page 又可能由 compund page 組成。

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

用 crash 實際讀取 skb 記憶體發現,有些 skb 存在 17 個 frag page,並且資料大小隻有 10 Byte。

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

解析 frag page 的 order 為 3,意味著一個 frag page 佔用 32k 記憶體。

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

極端情況下,一個 skb 可能佔用(1+17) 8=144 頁,上圖 slabinfo 中skbuff_head_cache 活躍 object 數量為 15033 個,所以理論最大總記憶體 =14415033*4K = 8.2G,而我們現在遇到的場景消耗 6G 的記憶體是完全有可能的。

—— 完 ——

加入龍蜥社群

加入微信群:新增社群助理-龍蜥社群小龍(微信:openanolis_assis),備註【龍蜥】與你同在;加入釘釘群:掃描下方釘釘群二維碼。歡迎開發者/使用者加入龍蜥社群(OpenAnolis)交流,共同推進龍蜥社群的發展,一起打造一個活躍的、健康的開源作業系統生態!

SysOM 案例解析:消失的記憶體都去哪了 !| 龍蜥技術

關於龍蜥社群

龍蜥社群(OpenAnolis)由企事業單位、高等院校、科研單位、非營利性組織、個人等在自願、平等、開源、協作的基礎上組成的非盈利性開源社群。龍蜥社群成立於 2020 年 9 月,旨在構建一個開源、中立、開放的Linux 上游發行版社群及創新平臺。

龍蜥社群成立的短期目標是開發龍蜥作業系統(Anolis OS)作為 CentOS 停服後的應對方案,構建一個相容國際 Linux 主流廠商的社群發行版。中長期目標是探索打造一個面向未來的作業系統,建立統一的開源作業系統生態,孵化創新開源專案,繁榮開源生態。

目前,Anolis OS 8.6已釋出,更多龍蜥自研特性,支援 X86_64 、RISC-V、Arm64、LoongArch 架構,完善適配 Intel、兆芯、鯤鵬、龍芯等晶片,並提供全棧國密支援。

歡迎加入我們,一起打造面向未來的開源作業系統!


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70004278/viewspace-2904643/,如需轉載,請註明出處,否則將追究法律責任。

相關文章