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 啟動核心),則以下輸出將被轉儲到系統日誌中
遇到的問題的描述
這將是系統日誌中的一條訊息,以
=============================================== 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 設定該選項)
如果涉及物件,則物件的內容。
各種型別的行可以跟隨 BUG SLUB 行
- Bytes b4 <地址><位元組>
顯示在檢測到問題之前物件的前幾個位元組。 如果損壞沒有停止於物件的開頭,則可能很有用。
- Object <地址><位元組>
物件的位元組。 如果物件不活動,則位元組通常包含毒化值。 任何非毒化值都顯示在釋放後被寫入損壞。
- Redzone <地址><位元組>
物件之後的紅色區域。 紅色區域用於檢測物件之後的寫入。 所有位元組應始終具有相同的值。 如果有任何偏差,則這是由於在物件邊界之後寫入造成的。
(僅當設定了 SLAB_RED_ZONE 時,紅色區域資訊才可用。slab_debug 設定該選項)
- Padding <地址><位元組>
未使用的資料,用於填充空間,以便正確對齊下一個物件。 在除錯情況下,我們確保至少有 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` 指令碼繪製其輸出。 因此,它將分析從檢視數字(大量數字)轉移到更簡單的內容 - 視覺化分析。
生成圖表
收集 slabinfo 擴充套件記錄,例如
while [ 1 ]; do slabinfo -X >> FOO_STATS; sleep 1; done
將統計檔案 (-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 個圖
收集您需要的 STATS1、STATS2、.. STATSN 檔案
while [ 1 ]; do slabinfo -X >> STATS<X>; sleep 1; done
預處理這些 STATS 檔案
slabinfo-gnuplot.sh STATS1 STATS2 .. STATSN
在‘-t’模式下執行
slabinfo-gnuplot.sh,傳遞所有生成的預處理的 *-totalsslabinfo-gnuplot.sh -t STATS1-totals STATS2-totals .. STATSN-totals
這將生成一個圖(png 檔案)。
可以預期的是,圖會很大,因此一些波動或小尖峰可能會被忽略。 為了處理這個問題,
slabinfo-gnuplot.sh有兩個選項可以“放大”/“縮小”-s %d,%d-- 覆蓋預設的影像寬度和高度-r %d,%d-- 指定要使用的樣本範圍(例如,在slabinfo -X >> FOO_STATS; sleep 1;的情況下,使用-r 40,60範圍將僅繪製在第 40 秒和第 60 秒之間收集的樣本)。
SLUB 的 DebugFS 檔案¶
有關啟用了使用者跟蹤除錯選項的 SLUB 快取的當前狀態的更多資訊,debugfs 檔案通常在 /sys/kernel/debug/slab/<cache>/ 下可用(僅為啟用了使用者跟蹤的快取建立)。 有 2 種類型的這些檔案,具有以下除錯資訊
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/0x3f0free_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 日