SLUB 簡短使用者指南

SLUB 的基本理念與 SLAB 非常不同。SLAB 需要重新構建核心才能啟用所有 slab 快取的除錯選項。SLUB 始終包含完整的除錯,但預設情況下是關閉的。SLUB 可以僅為選定的 slab 啟用除錯,以避免影響整體系統效能,這可能會使錯誤更難以找到。

為了開啟除錯,可以將選項 slab_debug 新增到核心命令列。這將為所有 slab 啟用完整除錯。

通常,然後會使用 slabinfo 命令來獲取統計資料並對 slab 執行操作。預設情況下,slabinfo 僅列出包含資料的 slab。 執行該命令時,請參閱“slabinfo -h”以獲取更多選項。slabinfo 可以編譯為

gcc -o slabinfo tools/mm/slabinfo.c

slabinfo 的某些操作模式要求在命令列上啟用 slub 除錯。例如,如果沒有開啟除錯,將沒有跟蹤資訊可用,如果未開啟除錯,則只能部分執行驗證。

slab_debug 的更多複雜用法:

可以向 slab_debug 提供引數。 如果未指定任何引數,則啟用完整除錯。 格式

slab_debug=<除錯選項>

為所有 slab 啟用選項

slab_debug=<除錯選項>,<slab 名稱 1>,<slab 名稱 2>,...

僅為選定的 slab 啟用選項(逗號後沒有空格)

可以為所有 slab 或選定的 slab 提供多個選項塊,選項塊之間用“;”分隔。 “所有 slab”塊的最後一個應用於除匹配“選定的 slab”塊之一的 slab 之外的所有 slab。 應用與 slab 名稱匹配的第一個“選定的 slab”塊的選項。

可能的除錯選項包括

F               Sanity checks on (enables SLAB_DEBUG_CONSISTENCY_CHECKS
                Sorry SLAB legacy issues)
Z               Red zoning
P               Poisoning (object and padding)
U               User tracking (free and alloc)
T               Trace (please only use on single slabs)
A               Enable failslab filter mark for the cache
O               Switch debugging off for caches that would have
                caused higher minimum slab orders
-               Switch all debugging off (useful if the kernel is
                configured with CONFIG_SLUB_DEBUG_ON)

例如,為了僅使用完整性檢查和紅色區域啟動,可以指定

slab_debug=FZ

嘗試查詢 dentry 快取中的問題?嘗試

slab_debug=,dentry

僅在 dentry 快取上啟用除錯。您可以在 slab 名稱的末尾使用星號,以覆蓋所有具有相同字首的 slab。例如,以下是如何毒化 dentry 快取以及所有 kmalloc slab 的方法

slab_debug=P,kmalloc-*,dentry

紅色區域和跟蹤可能會重新對齊 slab。我們可以僅對 dentry 快取應用完整性檢查

slab_debug=F,dentry

除錯選項可能需要增加最小可能的 slab 順序,以儲存元資料(例如,具有 PAGE_SIZE 物件大小的快取)。這更有可能導致低記憶體情況或記憶體高度碎片化時出現 slab 分配錯誤。 要預設關閉此類快取的除錯,請使用

slab_debug=O

您可以使用選項塊將不同的選項應用於不同的 slab 名稱列表。 這將為 dentry 啟用紅色區域,併為 kmalloc 啟用使用者跟蹤。 所有其他 slab 將不會啟用任何除錯

slab_debug=Z,dentry;U,kmalloc-*

您還可以為所有快取啟用選項(例如,完整性檢查和毒化),但排除一些被認為對效能至關重要且不需要除錯的快取,方法是指定全域性除錯選項,然後是帶有“-”作為選項的 slab 名稱列表

slab_debug=FZ;-,zs_handle,zspage

可以在以下位置的相應檔案中找到 slab 的每個除錯選項的狀態

/sys/kernel/slab/<slab name>/

如果檔案包含 1,則啟用該選項,0 表示停用。 slab_debug 引數中的除錯選項轉換為以下檔案

F       sanity_checks
Z       red_zone
P       poison
U       store_user
T       trace
A       failslab

failslab 檔案是可寫的,因此寫入 1 或 0 將在執行時啟用或停用該選項。 如果快取是別名,則寫入返回 -EINVAL。 小心跟蹤:如果在錯誤的 slab 上使用它,它可能會噴出大量資訊並且永遠不會停止。

Slab 合併

如果未指定任何除錯選項,則 SLUB 可能會將相似的 slab 合併在一起,以減少開銷並增加物件的快取熱度。 slabinfo -a 顯示哪些 slab 已合併在一起。

Slab 驗證

如果核心使用 slab_debug 啟動,則 SLUB 可以驗證所有物件。 為了做到這一點,您必須擁有 slabinfo 工具。 然後你可以這樣做

slabinfo -v

這將測試所有物件。 輸出將生成到系統日誌。

如果在沒有 slab 除錯的情況下啟動,這也可以在更有限的方式下工作。 在這種情況下,slabinfo -v 僅測試所有可訪問的物件。 通常,這些物件位於 cpu slab 和部分 slab 中。 在非除錯情況下,SLUB 不會跟蹤完整的 slab。

獲得更多效能

在某種程度上,SLUB 的效能受到需要不時獲取 list_lock 來處理部分 slab 的限制。 該開銷由每個 slab 的分配順序控制。 分配可以受到核心引數的影響

slab_min_objects

允許指定必須至少有多少物件適合一個 slab,分配順序才能被接受。 通常,slub 將能夠在 slab 上執行此數量的分配,而無需諮詢集中式資源 (list_lock),在這些資源中可能會發生爭用。

slab_min_order

指定 slab 的最小順序。 與 slab_min_objects 類似的效果。

slab_max_order

指定 slab_min_objects 不應再檢查的順序。 這對於避免 SLUB 嘗試生成超大型頁面以將 slab_min_objects 的具有大物件大小的 slab 快取放入一個高階頁面中非常有用。 設定命令列引數 debug_guardpage_minorder=N (N > 0) 會強制將 slab_max_order 設定為 0,這將導致最小可能的 slab 分配順序。

slab_strict_numa

在每次分配時啟用記憶體策略的應用。 這可以更準確地放置物件,這可能會減少對遠端節點的訪問。 預設是在獲取新頁面或從列表中檢索頁面時,僅在頁面級別應用記憶體策略。 啟用此選項會降低 slab 分配器的快速路徑效能。

SLUB 除錯輸出

這是一個 slub 除錯輸出的示例

====================================================================
BUG kmalloc-8: Right Redzone overwritten
--------------------------------------------------------------------

INFO: 0xc90f6d28-0xc90f6d2b. First byte 0x00 instead of 0xcc
INFO: Slab 0xc528c530 flags=0x400000c3 inuse=61 fp=0xc90f6d58
INFO: Object 0xc90f6d20 @offset=3360 fp=0xc90f6d58
INFO: Allocated in get_modalias+0x61/0xf5 age=53 cpu=1 pid=554

Bytes b4 (0xc90f6d10): 00 00 00 00 00 00 00 00 5a 5a 5a 5a 5a 5a 5a 5a ........ZZZZZZZZ
Object   (0xc90f6d20): 31 30 31 39 2e 30 30 35                         1019.005
Redzone  (0xc90f6d28): 00 cc cc cc                                     .
Padding  (0xc90f6d50): 5a 5a 5a 5a 5a 5a 5a 5a                         ZZZZZZZZ

  [<c010523d>] dump_trace+0x63/0x1eb
  [<c01053df>] show_trace_log_lvl+0x1a/0x2f
  [<c010601d>] show_trace+0x12/0x14
  [<c0106035>] dump_stack+0x16/0x18
  [<c017e0fa>] object_err+0x143/0x14b
  [<c017e2cc>] check_object+0x66/0x234
  [<c017eb43>] __slab_free+0x239/0x384
  [<c017f446>] kfree+0xa6/0xc6
  [<c02e2335>] get_modalias+0xb9/0xf5
  [<c02e23b7>] dmi_dev_uevent+0x27/0x3c
  [<c027866a>] dev_uevent+0x1ad/0x1da
  [<c0205024>] kobject_uevent_env+0x20a/0x45b
  [<c020527f>] kobject_uevent+0xa/0xf
  [<c02779f1>] store_uevent+0x4f/0x58
  [<c027758e>] dev_attr_store+0x29/0x2f
  [<c01bec4f>] sysfs_write_file+0x16e/0x19c
  [<c0183ba7>] vfs_write+0xd1/0x15a
  [<c01841d7>] sys_write+0x3d/0x72
  [<c0104112>] sysenter_past_esp+0x5f/0x99
  [<b7f7b410>] 0xb7f7b410
  =======================

FIX kmalloc-8: Restoring Redzone 0xc90f6d28-0xc90f6d2b=0xcc

如果 SLUB 遇到損壞的物件(完整檢測需要使用 slab_debug 啟動核心),則以下輸出將被轉儲到系統日誌中

  1. 遇到的問題的描述

    這將是系統日誌中的一條訊息,以

    ===============================================
    BUG <slab cache affected>: <What went wrong>
    -----------------------------------------------
    
    INFO: <corruption start>-<corruption_end> <more info>
    INFO: Slab <address> <slab information>
    INFO: Object <address> <object information>
    INFO: Allocated in <kernel function> age=<jiffies since alloc> cpu=<allocated by
       cpu> pid=<pid of the process>
    INFO: Freed in <kernel function> age=<jiffies since free> cpu=<freed by cpu>
       pid=<pid of the process>
    

    開頭(僅當為 slab 設定了 SLAB_STORE_USER 時,物件分配/釋放資訊才可用。slab_debug 設定該選項)

  2. 如果涉及物件,則物件的內容。

    各種型別的行可以跟隨 BUG SLUB 行

    Bytes b4 <地址><位元組>

    顯示在檢測到問題之前物件的前幾個位元組。 如果損壞沒有停止於物件的開頭,則可能很有用。

    Object <地址><位元組>

    物件的位元組。 如果物件不活動,則位元組通常包含毒化值。 任何非毒化值都顯示在釋放後被寫入損壞。

    Redzone <地址><位元組>

    物件之後的紅色區域。 紅色區域用於檢測物件之後的寫入。 所有位元組應始終具有相同的值。 如果有任何偏差,則這是由於在物件邊界之後寫入造成的。

    (僅當設定了 SLAB_RED_ZONE 時,紅色區域資訊才可用。slab_debug 設定該選項)

    Padding <地址><位元組>

    未使用的資料,用於填充空間,以便正確對齊下一個物件。 在除錯情況下,我們確保至少有 4 個位元組的填充。 這允許檢測物件之前的寫入。

  3. 堆疊轉儲

    堆疊轉儲描述了檢測到錯誤的位置。 透過檢視分配或釋放該物件的函式,可能更容易找到損壞的原因。

  4. 報告如何處理該問題以確保系統的持續執行。

    這些是系統日誌中的訊息,以

    FIX <slab cache affected>: <corrective action taken>
    

    開頭在上面的示例中,SLUB 發現活動物件的紅色區域已被覆蓋。 在這裡,一串 8 個字元被寫入長度為 8 個字元的 slab 中。 但是,8 個字元的字串需要一個終止符 0。該零已覆蓋紅色區域欄位的第一個位元組。 在報告遇到的問題的詳細資訊後,FIX SLUB 訊息告訴我們,SLUB 已將紅色區域恢復為其正確值,然後系統操作繼續。

緊急操作

可以透過使用以下方式啟動來啟用最小除錯(僅完整性檢查)

slab_debug=F

通常,這足以啟用 slub 的彈性功能,即使錯誤的核心元件會繼續損壞物件,也能使系統保持執行。 這對於生產系統可能很重要。 完整性檢查會影響效能,並且系統日誌中會持續顯示錯誤訊息,但不會使用額外的記憶體(與完整除錯不同)。

不提供保證。 仍然需要修復核心元件。 透過找到經歷損壞的 slab 並僅為該快取啟用除錯,可以進一步最佳化效能

slab_debug=F,dentry

如果損壞是透過在物件末尾之後寫入發生的,那麼建議啟用紅色區域,以避免損壞其他物件的開頭

slab_debug=FZ,dentry

擴充套件的 slabinfo 模式和繪圖

slabinfo 工具具有特殊的“擴充套件”('-X') 模式,其中包括
  • Slabcache 總計

  • 按大小排序的 Slab(最多 -N <數量> Slab,預設為 1)

  • 按損失排序的 Slab(最多 -N <數量> Slab,預設為 1)

此外,在此模式下,slabinfo 不會動態縮放大小 (G/M/K) 並且以位元組為單位報告所有內容(此功能也可透過 '-B' 選項用於其他 slabinfo 模式),這使得報告更加精確和準確。 此外,在某種意義上,-X’ 模式還簡化了對 slab 行為的分析,因為可以使用 ``slabinfo-gnuplot.sh` 指令碼繪製其輸出。 因此,它將分析從檢視數字(大量數字)轉移到更簡單的內容 - 視覺化分析。

生成圖表

  1. 收集 slabinfo 擴充套件記錄,例如

    while [ 1 ]; do slabinfo -X >> FOO_STATS; sleep 1; done
    
  2. 將統計檔案 (-s) 傳遞給 slabinfo-gnuplot.sh 指令碼

    slabinfo-gnuplot.sh FOO_STATS [FOO_STATS2 .. FOO_STATSN]
    

    slabinfo-gnuplot.sh 指令碼將預處理收集的記錄,併為每個 STATS 檔案生成 3 個 png 檔案(和 3 個預處理快取檔案): - Slabcache 總計:FOO_STATS-totals.png - 按大小排序的 Slab:FOO_STATS-slabs-by-size.png - 按損失排序的 Slab:FOO_STATS-slabs-by-loss.png

另一個用例是,當您需要比較“之前”和“之後”某些程式碼修改的 slab 行為時,slabinfo-gnuplot.sh 會很有用。 為了幫助您,slabinfo-gnuplot.sh 指令碼可以“合併”來自不同測量的 Slabcache 總計 部分。 要直觀地比較 N 個圖

  1. 收集您需要的 STATS1、STATS2、.. STATSN 檔案

    while [ 1 ]; do slabinfo -X >> STATS<X>; sleep 1; done
    
  2. 預處理這些 STATS 檔案

    slabinfo-gnuplot.sh STATS1 STATS2 .. STATSN
    
  3. 在‘-t’模式下執行 slabinfo-gnuplot.sh,傳遞所有生成的預處理的 *-totals

    slabinfo-gnuplot.sh -t STATS1-totals STATS2-totals .. STATSN-totals
    

    這將生成一個圖(png 檔案)。

    可以預期的是,圖會很大,因此一些波動或小尖峰可能會被忽略。 為了處理這個問題,slabinfo-gnuplot.sh 有兩個選項可以“放大”/“縮小”

    1. -s %d,%d -- 覆蓋預設的影像寬度和高度

    2. -r %d,%d -- 指定要使用的樣本範圍(例如,在 slabinfo -X >> FOO_STATS; sleep 1; 的情況下,使用 -r 40,60 範圍將僅繪製在第 40 秒和第 60 秒之間收集的樣本)。

SLUB 的 DebugFS 檔案

有關啟用了使用者跟蹤除錯選項的 SLUB 快取的當前狀態的更多資訊,debugfs 檔案通常在 /sys/kernel/debug/slab/<cache>/ 下可用(僅為啟用了使用者跟蹤的快取建立)。 有 2 種類型的這些檔案,具有以下除錯資訊

  1. alloc_traces

    Prints information about unique allocation traces of the currently
    allocated objects. The output is sorted by frequency of each trace.
    
    Information in the output:
    Number of objects, allocating function, possible memory wastage of
    kmalloc objects(total/per-object), minimal/average/maximal jiffies
    since alloc, pid range of the allocating processes, cpu mask of
    allocating cpus, numa node mask of origins of memory, and stack trace.
    
    Example:::
    
    338 pci_alloc_dev+0x2c/0xa0 waste=521872/1544 age=290837/291891/293509 pid=1 cpus=106 nodes=0-1
        __kmem_cache_alloc_node+0x11f/0x4e0
        kmalloc_trace+0x26/0xa0
        pci_alloc_dev+0x2c/0xa0
        pci_scan_single_device+0xd2/0x150
        pci_scan_slot+0xf7/0x2d0
        pci_scan_child_bus_extend+0x4e/0x360
        acpi_pci_root_create+0x32e/0x3b0
        pci_acpi_scan_root+0x2b9/0x2d0
        acpi_pci_root_add.cold.11+0x110/0xb0a
        acpi_bus_attach+0x262/0x3f0
        device_for_each_child+0xb7/0x110
        acpi_dev_for_each_child+0x77/0xa0
        acpi_bus_attach+0x108/0x3f0
        device_for_each_child+0xb7/0x110
        acpi_dev_for_each_child+0x77/0xa0
        acpi_bus_attach+0x108/0x3f0
    
  2. free_traces

    Prints information about unique freeing traces of the currently allocated
    objects. The freeing traces thus come from the previous life-cycle of the
    objects and are reported as not available for objects allocated for the first
    time. The output is sorted by frequency of each trace.
    
    Information in the output:
    Number of objects, freeing function, minimal/average/maximal jiffies since free,
    pid range of the freeing processes, cpu mask of freeing cpus, and stack trace.
    
    Example:::
    
    1980 <not-available> age=4294912290 pid=0 cpus=0
    51 acpi_ut_update_ref_count+0x6a6/0x782 age=236886/237027/237772 pid=1 cpus=1
        kfree+0x2db/0x420
        acpi_ut_update_ref_count+0x6a6/0x782
        acpi_ut_update_object_reference+0x1ad/0x234
        acpi_ut_remove_reference+0x7d/0x84
        acpi_rs_get_prt_method_data+0x97/0xd6
        acpi_get_irq_routing_table+0x82/0xc4
        acpi_pci_irq_find_prt_entry+0x8e/0x2e0
        acpi_pci_irq_lookup+0x3a/0x1e0
        acpi_pci_irq_enable+0x77/0x240
        pcibios_enable_device+0x39/0x40
        do_pci_enable_device.part.0+0x5d/0xe0
        pci_enable_device_flags+0xfc/0x120
        pci_enable_device+0x13/0x20
        virtio_pci_probe+0x9e/0x170
        local_pci_probe+0x48/0x80
        pci_device_probe+0x105/0x1c0
    

Christoph Lameter,2007 年 5 月 30 日 Sergey Senozhatsky,2015 年 10 月 23 日