内存管理-54-slub-1-文档翻译等


一、slub.rst 翻译

注: 翻译自 msm-5.4/Documentation/vm/slub.rst

1. SLUB 简明用户指南

SLUB 的基本理念与 SLAB 截然不同。SLAB 需要重新编译内核才能激活所有 slab 缓存的调试选项。SLUB 始终包含完整的调试功能,但默认情况下处于关闭状态####。SLUB 可以仅对选定的 slab 启用调试####,以避免影响系统整体性能,从而降低错误查找的难度。

要启用调试功能,可以在内核命令行中添加 ``slub_debug`` 选项。这将为所有 slab 启用完整的调试功能。

通常,用户会使用 ``slabinfo`` 命令来获取统计数据并对 slab 执行操作。默认情况下,``slabinfo`` 仅列出包含数据的 slab。运行命令时,请参阅“slabinfo -h”以了解更多选项。``slabinfo`` 可以使用以下方式编译:

gcc -o slabinfo tools/vm/slabinfo.c

``slabinfo`` 的某些操作模式要求在命令行中启用 slub 调试。例如,如果未启用调试,则无法获取任何跟踪信息;如果未启用调试,则只能执行部分验证。


1.1 slub_debug 的一些更高级用法

可以为 ``slub_debug`` 传递参数。如果未指定任何参数,则启用完整调试。格式:

slub_debug=<Debug-Options> //为所有 slab 启用Debug选项

slub_debug=<Debug-Options>,<slab name1>,<slab name2>,... //仅为选定的 slab 启用Debug选项(逗号后无空格)

可用的调试选项如下:

F 启用完整性检查(启用 SLAB_DEBUG_CONSISTENCY_CHECKS,抱歉,SLAB 存在一些遗留问题).

Z 红色区域标记

P 中毒(对象和填充)

U 用户跟踪(释放和分配)

T 跟踪(请仅在单个 slab 上使用)

A 切换缓存的 failedslab 过滤器标记

O 关闭会导致 slab 最小order过高的缓存的调试功能

- 关闭所有调试功能(如果内核配置了 CONFIG_SLUB_DEBUG_ON,则此选项有用)

例如,要仅使用完整性检查和红色区域进行启动,可以指定:

slub_debug=FZ

想在目录项缓存中查找问题?试试:

slub_debug=,dentry

这样就只对目录项缓存启用调试。您可以在 slab 名称末尾使用星号,以覆盖所有具有相同前缀的 slab####。例如,以下是如何poison目录项缓存以及所有 kmalloc slab:

slub_debug=P,kmalloc-*,dentry

红色区域和跟踪可能会重新排列 slab。我们可以仅对目录项缓存应用完整性检查,如下所示:

slub_debug=F,dentry

由于存储元数据(例如,对象大小为 PAGE_SIZE 的缓存),调试选项可能需要增加最小可能的 slab order。在内存不足或内存碎片化严重的情况下,这更容易导致 slab 分配错误。要默认关闭此类缓存的调试功能,请使用:

slub_debug=O

如果您忘记在内核命令行中启用调试:可以在内核启动后手动启用调试####。查看以下内容:

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

查看可写文件。向其中写入 1 将启用相应的调试选项。所有选项都可以在不包含对象的 slab 上设置。如果 slab 已包含对象,则只能启用完整性检查和跟踪####。其他选项可能会导致对象重新对齐。

谨慎使用跟踪功能:如果在错误的 slab 上使用,它可能会输出大量信息并且永不停止。


2. slab 合并

如果没有指定调试选项,SLUB 可能会将相似的 slab 合并在一起,以减少开销并提高对象的缓存热度####。

slabinfo -a //注:实测没有任何打印。

会显示哪些 slab 被合并了。


3. slab 验证

如果内核启动时启用了 slub_debug,SLUB 可以验证所有对象。为此,您必须拥有 slabinfo 工具。然后,您可以执行以下操作:TODO: 试一下

slabinfo -v //注:实测没有任何打印。

这将测试所有对象。输出将生成到系统日志(syslog)。

如果启动时未启用 slab debug,则此方法也以更有限的方式工作。在这种情况下,slabinfo -v 只会测试所有可达对象。通常,这些对象位于 CPU slab 和部分 slab 中。在非调试情况下,SLUB 不会跟踪完整的 slab。


4. 提升性能

SLUB 的性能在一定程度上受限于偶尔需要获取 list_lock 来处理部分 slab。这种开销取决于每个 slab 的分配order。分配order可以通过内核参数来影响:

slub_min_objects=x(默认值 4)
slub_min_order=x(默认值 0)
slub_max_order=x(默认值 3 (PAGE_ALLOC_COSTLY_ORDER))

slub_min_objects:

允许指定一个 slab 中至少需要容纳多少个对象,才能保证分配order合理。通常情况下,slub 能够在 slab 上执行此数量的分配,而无需查询可能出现争用的集中式资源(list_lock)。

slub_min_order:

指定 slab 的最小order。其作用类似于 slub_min_objects。

slub_max_order:

指定不再检查 slub_min_objects 的order。这有助于避免 SLUB 尝试生成超大的order页,以便将具有较大对象大小的 slab 缓存中的 slub_min_objects 放入一个高order页中。设置命令行参数 debug_guardpage_minorder=N (N > 0) 会强制将 slub_max_order 设置为 0,从而实现 slab 分配的最小order。


5. 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 检测到损坏的对象(完整检测需要使用 slub_debug 启动内核),则会将以下输出写入系统日志:


5.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 设置为 true 时,才能获取对象分配/释放信息。slub_debug 会设置该选项。*/


5.2 如果涉及对象,则显示对象的内容。

BUG SLUB 行之后可能跟随以下类型的行:

Bytes b4 <address> : <bytes>

显示检测到问题的对象之前的几个字节。如果损坏并未在对象开头停止,则此行非常有用。

Object <address> : <bytes>

对象的字节。如果对象处于 inactive 状态(释放后),则这些字节通常包含 poison 值。任何 non-poison 值 都表明存在因释放后写入而导致的损坏。

Redzone <address> : <bytes>

对象之后的 Redzone。Redzone 用于检测对象之后的写入。所有字节的值应始终相同。如果存在任何偏差,则表示在对象边界之后发生了写入。

仅当设置了 SLAB_RED_ZONE 时,Redzone 信息才可用。slub_debug 会设置该选项。

Padding <address> : <bytes>

未使用的数据用于填充空间,以便使下一个对象正确对齐。在调试模式下,我们确保至少有 4 个字节的填充。这样可以检测到对象之前的写入操作。


5.3 堆栈转储

堆栈转储描述了检测到错误的位置。通过查看分配或释放对象的函数,更有可能找到损坏的原因。


5.4 报告如何处理问题以确保系统持续运行。

这些是系统日志中以以下形式开头的消息:

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

在上面的示例中,SLUB 发现活跃对象的 Redzone 已被覆盖。这里,一个 8 个字符的字符串被写入了一个长度为 8 个字符的 slab。然而,一个 8 个字符的字符串需要一个终止符 0。这个 0 覆盖了 Redzone 字段的第一个字节。在报告遇到的问题的详细信息后,FIX SLUB 消息告诉我们,SLUB 已将 Redzone 恢复到其正确值,然后系统运行继续进行。


6. 紧急操作

可通过以下方式启用最小调试(仅进行完整性检查):

slub_debug=F

这通常足以启用 slub 的恢复功能,即使内核组件损坏导致对象持续丢失,也能保证系统持续运行。这对于生产系统可能至关重要。完整性检查会影响性能,并且系统日志中会持续输出错误消息,但不会占用额外内存(与完整调试不同)。

不保证一定有效。内核组件仍需修复。可以通过定位出现损坏的缓存块并仅针对该缓存启用调试来进一步优化性能。

例如:

slub_debug=F,dentry

如果损坏是由于在对象末尾写入数据造成的,则建议启用 Redzone 以避免损坏其他对象的开头。####

slub_debug=FZ,dentry


7. 扩展的 slabinfo 模式和绘图

slabinfo 工具有一个特殊的“扩展”(`-X`)模式,其中包括:

- Slabcache 总计

- 按大小排序的 Slab(最多 -N <num> 个 Slab,默认为 1)

- 按损失排序的 Slab(最多 -N <num> 个 Slab,默认为 1)

此外,在此模式下,slabinfo 不会动态缩放大小(G/M/K),而是以字节为单位报告所有数据(其他 slabinfo 模式也可通过 `-B` 选项使用此功能),这使得报告更加精确。而且,在某种程度上,`-X` 模式也简化了对 Slab 行为的分析,因为它的输出可以使用 slabinfo-gnuplot.sh 脚本进行绘图####。因此,它将分析工作从查看海量数据转移到更简便的方式——可视化分析。

生成图表:

a) 收集 slabinfo 扩展记录,例如:

while [ 1 ]; do slabinfo -X >> FOO_STATS; sleep 1; done

b) 将统计文件(-s)传递给 ``slabinfo-gnuplot.sh`` 脚本::

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

slabinfo-gnuplot.sh 脚本将预处理收集到的记录,并为每个 STATS 文件生成 3 个 png 文件(以及 3 个预处理缓存文件):

- Slab 缓存总计:FOO_STATS-totals.png

- 按大小排序的 Slab:FOO_STATS-slabs-by-size.png

- 按损失排序的 Slab:FOO_STATS-slabs-by-loss.png

slabinfo-gnuplot.sh 的另一个用途是,当您需要比较代码修改前后 Slab 的行为时。为了方便您使用,slabinfo-gnuplot.sh 脚本可以合并来自不同测量的 `Slabcache Totals` 部分。要直观地比较 N 个图表:

a) 收集所需数量的 STATS1、STATS2、.. STATSN 文件:

while [ 1 ]; do slabinfo -X >> STATS<X>; sleep 1; done

b) 预处理这些 STATS 文件:

slabinfo-gnuplot.sh STATS1 STATS2 .. STATSN

c) 以 `-t` 模式执行 slabinfo-gnuplot.sh,并传递所有生成的预处理后的 *-totals:

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

这将生成一个图表(png 文件)。

不出所料,图表可能很大,因此一些波动或小的尖峰可能难以察觉。为了解决这个问题,slabinfo-gnuplot.sh 提供了两个“放大”/“缩小”选项:

a) ``-s %d,%d`` -- 覆盖默认的图像宽度和高度

b) ``-r %d,%d`` -- 指定要使用的样本范围(例如,在 ``slabinfo -X >> FOO_STATS; sleep 1;`` 的情况下,使用 ``-r 40,60`` 范围将仅绘制第 40 秒到第 60 秒之间采集的样本)。

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


二、slabinfo-gnuplot.sh

注:文件位置 msm-5.4/tools/vm/slabinfo-gnuplot.sh

1. chatGPT: Linux内核源码中 slabinfo-gnuplot.sh 的作用

slabinfo-gnuplot.sh 是一个用于将 Linux 内核 slab 分配器的性能数据可视化的 Shell 脚本工具。它通过解析 /proc/slabinfo 文件获取内存分配器的实时统计信息,然后调用 gnuplot 生成直观的图表,帮助开发者和系统管理员分析内核内存使用效率、内存碎片情况以及 slab 缓存的性能瓶颈。

该脚本的核心实现逻辑与文档中提到的 vmstat 数据处理脚本类似,都遵循“数据采集-格式化-可视化”的三步流程。首先从内核提供的 slabinfo 接口提取关键指标(如对象数量、已分配大小、利用率等),然后通过文本处理工具(如 awk、sed)将原始数据转换为 gnuplot 可识别的格式,最后自动生成折线图、柱状图等可视化报告,直观展示 slab 缓存的动态变化趋势。

与手动分析 slabinfo 原始数据相比,该脚本的主要价值在于降低了内核内存调试的门槛。用户无需深入理解 slab 分配器的复杂实现细节,即可通过图表快速定位内存泄漏、缓存利用率低下等问题。例如,持续增长的对象数量曲线可能暗示内存泄漏,而剧烈波动的分配/释放频率则可能反映应用程序的内存管理缺陷。

使用时,通常需要配合定时任务(如 cron)或性能测试工具运行,以捕获不同负载条件下的 slab 行为特征。生成的图表可用于性能优化决策,例如调整 slab 缓存大小、优化对象复用策略等。与文档中的 vmstat 处理脚本类似,它体现了 Linux 系统管理中“命令行工具+脚本+可视化”的经典组合,将原始性能数据转化为可操作的 insights。

值得注意的是,这类工具通常需要 root 权限才能访问完整的 slabinfo 数据,且生成的图表类型(如时间序列图、对比柱状图)可通过修改脚本中的 gnuplot 配置命令进行定制。对于复杂的内核内存问题,其输出可与 dstat、perf 等工具的结果交叉验证,形成更全面的性能分析结论。


2. 测试

车机上执行:

# while [ 1 ]; do slabinfo -X >> tmp.txt; sleep 1; done

Ubuntu上执行 "slabinfo-gnuplot.sh tmp.txt" 会生成 tmp.txt-slabs-by-loss tmp.txt-slabs-by-size tmp.txt-totals 三个文件,内容分别为

android/frameworks/native/my_krn_test$ cat tmp.txt-slabs-by-loss
vm_area_struct 172457984 107471768
vm_area_struct 172457984 107471304
...
kmalloc-128 130318336 107026176
...
android/frameworks/native/my_krn_test$ cat tmp.txt-slabs-by-size
vm_area_struct 172457984 107471768
vm_area_struct 172457984 107471304
...
android/frameworks/native/my_krn_test$ cat tmp.txt-totals
1107619840 752163192
1107644416 752208536
...


三、slabinfo 等文件编译

Androd系统中在 source/lunch 后,在 msm-5.4/tools/vm 目录下 make 编译出的 page_owner_sort page-types slabinfo 是 x86-64 的,这里使用 Android.bp 编译Arm64的。

将 tools/vm 中的所有内容拷贝到 my_krn_test 目录下,page-types.c 依赖 tool/lib/api 也一并拷过来,然后按实现 Android.bp 修复编译报错,进行编译。文件如下:

~/android/frameworks/native$ tree my_krn_test
my_krn_test
├── Android.bp
├── api
│   ├── cpu.c
│   ├── cpu.h
│   ├── debug.c
│   ├── debug.h
│   ├── debug-internal.h
│   ├── fd
│   │   ├── array.c
│   │   └── array.h
│   └── fs
│       ├── fs.c
│       ├── fs.h
│       ├── tracing_path.c
│       └── tracing_path.h
├── page_owner_sort.c
├── page-types.c
├── slabinfo.c
└── slabinfo-gnuplot.sh


1. Android.bp

cc_binary {
    name: "slabinfo_static",
    srcs: [
        "slabinfo.c",
    ],
    defaults: ["cc_krn_test_build_defaults"],
}

cc_binary {
    name: "page_owner_sort_static",
    srcs: [
        "page_owner_sort.c",
    ],
    defaults: ["cc_krn_test_build_defaults"],
}

cc_binary {
    name: "page_types_static",
    srcs: [
        "page-types.c",
        "api/**/*.c",
    ],
    defaults: ["cc_krn_test_build_defaults"],
}

cc_defaults {
    name: "cc_krn_test_build_defaults",
    local_include_dirs: ["api", "fs"],
    cflags: [
        "-Wall",
        "-Werror",
        "-Wno-parentheses",
        "-Wno-int-conversion",
        "-Wno-pointer-arith",
        "-Wno-unused-function",
        "-Wno-unused-parameter",
        "-Wno-unused-variable",
    ],
    static_libs: [
        "libc",
        "libutils",
        "libcutils",
        "liblog",
   ],
   static_executable: true,
}

这里编译为静态链接,方便跨平台使用。

编译中有较多报错,cflags 改了几次。page-types.c 中的头文件要改为这样:

#include <linux/magic.h>
#include <linux/kernel-page-flags.h>
#include "fs.h"

将 str_error_r() 函数拷贝到 tracing_path.c 中。

 

posted on 2025-12-02 18:39  Hello-World3  阅读(0)  评论(0)    收藏  举报

导航