Slab,小物件也能搞出大事情

2021-09-20 09:19:10 字數 3997 閱讀 7052

排查記憶體使用(洩露、耗盡)問題的乙個的技巧是,區分「批發商」和「零售商」這兩類不同的記憶體管理機制。這裡的「批發商」,指的是按頁面管理並分配記憶體的機制。而「零售商」,則是指從「批發商」那裡批量獲取資源,並以位元組為單位,管理和分配記憶體的機制。

「零售商」和「批發商」的區分很重要。這是因為通過「零售商」分配出去的記憶體資源,在「批發商」那裡或多或少都有統計。但是從「批發商」那邊分配出去的記憶體資源,「零售商」幾乎一無所知。凡是一些詭異的,「我的記憶體去**了」的問題,往往都跟這個有點有關係。

「批發商」「零售商」的例子很多,比如windows上的一對介面virtualalloc和heapalloc。而在linux核心中,buddy system毋庸置疑是最大記憶體的「批發商」,而slab則是最常用的「零售商」。今天這篇文章,我跟大家分享乙個和「零售商」slab有關的真實案例。

今天的案例,我們從雲監控說起。雲監控作為乙個監控工具,給客戶提供了非常豐富的功能。阿里雲的客戶可以使用雲監控來隨時了解雲服務的狀態,而且在雲服務出現異常的時候,客戶可以及時地收到來自雲監控的報警資訊。

做為阿里雲技術支援的同學,可以說我們對雲監控有著一種非常「特殊」的感情。這是因為,很多時候客戶提給我們的問題,會以乙個雲監控的截圖開始,看到雲監控的截圖,那基本就等於我們有新的問題需要處理了。

以上的截圖是雲監控通過釘釘訊息傳送給客戶的報警資訊。從這個截圖裡來看,客戶的一台ecs例項記憶體使用率超過了90%,而且持續了1分鐘時間。這對ecs伺服器來說,是相當不健康的狀態。

處理記憶體使用問題,有一些基本套路。首先,我們可以使用free或top命令看一下系統總體記憶體使用情況,如物理記憶體大小,有多少剩餘記憶體,有多少被cache(這裡的cache和slab機制的cache是兩回事)和buff用掉了;其次,我們可以使用ps命令看看每個程序記憶體的使用情況;如果到這一步還沒有定位到問題,進一步,我們可以通過檢視/proc/meminfo檔案,在更細的粒度上了解記憶體資源的分布與分配情況。按照這個套路打下來,一般我們都會得到乙個初步的排查結果。以今天這個問題為例,系統32g記憶體幾乎被用光了,但是cache和buff並不大,程序使用的記憶體也不多。最終在meminfo檔案中,我發現有大量記憶體被slab用掉了。

slab應該算是linux記憶體管理機制中,最著名的「零售商」機制。寫slab的文章非常多,我這裡只做簡單的介紹。

做為「零售商」,slab肯定需要解決兩個問題,乙個是怎麼樣從「批發商」buddy system那裡「批發」記憶體,另外乙個是怎麼樣管理並把這些記憶體「散賣」出去。為了回答這兩個問題,我們首先理解一下slab機制的資料結構。slab機制包括三個層次的資料結構:cache,slab和object。如下圖,乙個slab一般是乙個4k大小的記憶體頁面。當我們把slab頁面,按照固定大小切分成能夠被「零售」的小塊的時候,我們就得到了很多的object。當把包含相同大小object的slab用「箭頭」串起來的時候,就形成了cache。這裡slab和object是比較容易理解的:因為把4k的頁面分配給幾個位元組的物件使用太浪費記憶體了,所以就需要切開來「散賣」。而cache的存在意義其實是比較隱晦的。cache存在的意義,其實是為了動態的管理slab,讓slab可以動態的增加和減少。在object需求量大的時候,可以從「批發商」那裡拿到很多的slab放進cache裡,而在用完之後,又可以動態地把slab退回去。

基於cache, slab和object資料結構,slab機制要解決上邊那兩個問題就變得很容易了:一方面,slab機制從「批發商」buddy system那裡,以slab(頁面)為單位獲取記憶體;另外一方面,slab機制以object為基本管理單元,把記憶體資源「零售」給系統中其他模組。

slabtop是乙個可以用來檢視slab記憶體使用情況的工具。下邊這個截圖來自客戶問題現場。我們可以根據cache size這一列,快速地定位到問題。很明顯cache size最大的一行對應的物件是dentry。

注意:為了解釋簡單,這裡我假設slab是乙個頁面,也就是4k大小。這個假設意味著,所有被管理的物件都是小於4k的。但在實際情況中,為了提供大於4k的記憶體塊,乙個slab其實可以是多個連續的物理頁面。

目錄項dentry算是乙個非常小的核心資料結構。從上邊的截圖我們可以看到,dentry其中只有0.19k。雖然dentry很小,但是這個結構是核心中最常使用的資料結構。如下圖,當程序開啟檔案的時候,一般都會有對應的dentry被分配出來。在linux這樣的「一切皆檔案」的作業系統裡,dentry結構被使用頻率是不言自明的。

因為已經定位到了核心資料結構dentry,所以我選擇抓乙個dump來慢慢分析這個問題。抓了dump之後讓客戶重啟機器釋放記憶體,以免導致更嚴重的後果,影響業務。

使用core dump排查問題有兩個優勢:一是在生產環境中,客戶有時候真的很難給我們機會去使用一些trace和工具一步一步的排查問題,而抓dump對業務的影響較小,抓完之後可以慢慢分析。二是core dump裡包含了系統中所有正常、不正常的狀態。資料量大且完整,這不是一般的資料收集方法可以比的。下邊我演示一遍怎麼用core dump來排查slab洩露問題。

使用sys命令,我們可以快速看一下客戶這台機器的基本資訊。如機器名,核心版本,記憶體大小等。

因為這個問題是記憶體使用問題,我們可以用kmem -i命令在dump裡看一下記憶體的使用情況。我們可以看到,這個系統有32g記憶體,其中slab用掉27g。

接下來我們看一下slab的使用情況。命令kmem -s可以幫我們列出核心中所有的slab cache。如下圖,每一行對應乙個cache,每個cache負責管理乙個物件,第二列是物件的名字。

既然已經發現了問題是由dentry物件引起的,那麼,我們可以到slab結構內部去檢視洩露的的dentry。命令kmem -s dentry可以幫我們列出包含dentry的所有slab。可以對照「零售商slab」一節第乙個插圖來看下邊的截圖。前兩行給出了cache的一些基本資訊,如位址,object名字,object大小等。從第三行開始,這個命令分塊輸出cache中所有的slab結構。每個slab裡包含一列小物件,這些物件都在乙個頁面內部,從他們連續的位址可以看出這點。實際上這個系統分配了7百萬slab頁面給dentry,下圖只截了前兩個。

這個時候問題來了。這個cache裡有7百萬slab,有1億4千萬dentry項,我們怎麼去分析這些項呢?其實有乙個很簡單的技巧,就是隨機挑選的一些dentry,然後看看這些dentry項裡,有沒有什麼共同的特徵,比如相同的字串,或者共同引用的位址等。對這個問題來說,我是很幸運的。我發現在隨機挑選的dentry結構裡,d_iname這個字串都包含乙個子串「doesnotexist_.db」。

使用doesnotexist_.db這個子串,我們可以在公網上輕而易舉地定位到下邊這個問題:這是nss-softokn軟體包的乙個bug。

技術支援工作的乙個創新點,也是這個工種的一大樂趣,是可以利用各種組合技,利用不同的方法,巧妙地組合並找出問題的答案。以今天這個問題為例,我們開始使用了一些基礎的排查工具對問題進行了初步的定位;進而我們使用了core dump這種看起來不那麼易用的工具,從記憶體中挖出了乙個字串作為線索;最後結合公網上的資訊,我們拼出了這個問題的答案。

馬雲創業真經 沒錢也能創出大事業

沒錢也能創出大事業 很多人都想創業,但他們似乎又有乙個不創業的理由 我沒有錢,我要是有錢的話,怎麼怎麼樣 似乎只要有錢,他就一定能創業成功。可是馬雲的創業經歷告訴我們 沒錢,同樣可以創業,同樣可以創出一番偉大的事業。馬雲有過三次創業經歷,創業開始時都沒什麼錢。第一次 創辦海博翻譯社 馬雲之所以要辦翻...

無需開發經驗 也能擁有小程式

本文分享嘉賓 毛帥,又拍管家資深開發工程師,主要負責又拍管家 圖管小程式第三方平台 圖管小程式等專案的開發 維護及拓新工作。熟悉 js c 等語言,有豐富的 nodejs 開發經驗,熱衷於研究高可用服務架構及底層實現原理。以下是分享內容 首先我來介紹一下又拍雲和又拍管家之間的關係。又拍管家是延續又拍...

PHP clone關鍵字 物件也能被「轉殖」

物件的複製是通過關鍵字 clone 來實現的。用 clone 轉殖出來的物件與原物件沒有任何關係,它是把原來的物件從當前的位置重新複製了乙份,也就是相當於在記憶體中新開闢了一塊空間。通過關鍵字 clone 可以轉殖乙個物件,語法格式如下 轉殖物件名稱 clone 原物件名稱 物件的 clone 方法...