本文转载自一位专家朋友(微博@vmunix)的博客,他从事UNIX性能方面的工作近十年,今年来开始研究Linux。原文地址:http://linuxperf.com/?p=184
在前文中,我们介绍了在RHEL6及较早的kernel上诊断slab泄漏问题的两种方法,可以说相当麻烦了,这是因为以前的slab没有提供原生的故障诊断机制。Linux
kernel自2.6.23之后采用的Slub自带了故障诊断机制,就方便很多,然而习惯上仍然把slub称作slab。如何判断你的系统kernel是否在用slub呢?仅从kernel版本号是看不准的,比如RHEL6的kernel版本2.6.x仍然在使用slab,从RHEL7才开始采用Slub。有一个简单的判断方法,就是看是否存在/sys/kernel/slab目录,有就是slub,没有就是slab。
Slub的debug机制
了解slub的debug机制之前,先要弄清楚可能会出现哪些问题,无论是slab还是slub,问题无非是以下几类:
-
内存泄露(leak),alloc之后忘了free,导致内存占用不断增长;
-
越界(overrun),访问了alloc分配的区域之外的内存,覆盖了不属于自己的数据;
-
使用已经释放的内存(use after free),正常情况下,已经被free释放的内存是不应该再被读写的,否则就意味着程序有bug;
-
使用未经初始化的数据(use uninitialised bytes),缺省模式下alloc分配的内存是不被初始化的,内存值是随机的,直接使用的话后果可能是灾难性的。
Slub提供了red zone和poisoning等debug机制来检测以上问题。
-
Red zone来自于橄榄球术语,是指球场底线附近的区域,slub通过在每一个对象后面额外添加一块red zone区域来帮助检测越界(overrun)问题,在red zone里填充了特征字符,如果代码访问到了red zone就意味着越界了。
-
Poisoning是通过往slub对象中填充特征字符的方式来检测use-after-free、use-uninitialised等问题,比如在分配slub对象时填充0x5a,在释放时填充0x6b,然后debug代码检查时如果看到本该是0x6b的位置变成了别的内容,就可能是发生了use-after-free,而本该是0x5a的位置如果变成了其它内容就意味着可能是use-uninitialised问题。更多的poison字节定义参见以下文件:
/lib/modules/$(uname -r)/build/include/linux/poison.h
(图)SLUB对象的格式
* 绿色的 Payload表示分配出去的 slub object;
* slub debug机制需要占用额外的内存,比如 Red zone,还有,为了追溯 slub object的分配和释放过程,需要额外的空间来存放 stack trace,即图中的 Tracking/Debugging;
* 图中的 FP是 Free Pointer的缩写,处于 free状态的 object是以链表的形式串在一起的,FP就是链表指针。
怎样开启slub debug
Slub本身包含了完整的debug功能,缺省是关闭的,需要的时候打开就行了。
开启slub debug有两种方式:
-
【启动时开启】
在kernel command line中加入以下参数:
slub_debug=
,
它会在重启时生效。
注:
slub_debug后面不跟任何参数表示打开所有的debug功能;
slub_debug=
对所有的slab打开指定的debug options;
slub_debug=
,
对指定的slab打开指定的debug options;
slub_debug=,
对指定的slab打开所有的debug options。
-
【运行中开启】
在运行系统上可以通过以下文件对指定的slab打开指定的debug option:
/sys/kernel/slab/
/
Debug options如下:
开启debug option会降低系统性能,所以尽量只开启必要的选项。
-
如果slab出现data corruption问题,可以考虑read_zone,poison,store_user,sanity_checks;
-
如果某个slab的大小持续疯涨,则可能是leak(内存泄露),可以开启trace,观察统计slab的alloc/free情况,寻找线索。
对于内核内存泄漏问题,还有另一个工具可供选用,详见:用KMEMLEAK检测内核内存泄漏
在kernel
commandline中开启slub debug与在运行系统上开启是有区别的。有些debug
option不能在运行系统上开启,比如red_zone、store_user需要额外的存储空间来保存debug信息,如果不是从一开始就开启,那么以前分配出去的slub对象就没有debug数据区,会导致对齐问题,而且debug代码也很难分辨新老slub对象;再比如poison,如果不是从一开始就开启,那么以前分配出去的slub对象就没有填充特征字符,debug代码也辨别不了哪些slub对象是有填充的、哪些是没有填充的。所以,在运行系统上开启slub
debug,如果指定的slab里面已经有了object,那么只能动态开启sanity_checks和trace,惟有当指定的slab还是空的,其它的debug
option才可以动态开启。我们实际使用的时候,尽管去试好了,如果某个debug option不允许动态开启,命令就不会成功,比如:
要进行slub debug,有一个重要特性不可不知,那就是slab merging。
Slab merging
很多slab的大小和参数是相似的,slub会把这些不同的slab合并到一起,好处是可以减少内存碎片,提高内存使用效率,这个称为slab merging(合并)。
怎么知道一个slab有没有发生合并呢?通过查看以下文件:
/sys/kernel/slab/
/aliases
aliases表示参与合并的slab的数量(自己除外),如果大于0就意味着发生了合并。
slabinfo工具(见下一节的介绍)可以具体列出哪些slab合并到了一起:
Slab merging会干扰debug,因为不同的slab合并到了一起,出了问题以后很难分辨是哪一个slab导致的。关闭slab merging的方法有两个:
-
在kernel command line中加入“slub_nomerge”;
-
开启slub debug之后,slab merging就会自动关闭。如果slub_debug指定了某个slab,那么只有指定的slab会关闭merging。
注:最好是在启动时开启slub_debug,如果是在运行系统上通过sysfs开启slub debug,那么之前已经合并的那些slab仍然会保持合并状态。
slabinfo工具
随内核源程序提供了一个slabinfo工具,但是需要自己手工编译。源程序的位置是在源代码树下的 tools/vm/slabinfo.c,编译方法是:
$ gcc -o slabinfo tools/vm/slabinfo.c
或者进入 tools/vm 目录下直接执行make:
$ make slabinfo
slabinfo工具能做的事情见它的帮助信息:
参考资料:
https://www.kernel.org/doc/Documentation/vm/slub.txt
https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-kernel-slab
https://lwn.net/Articles/340267/
https://gitlab.eurecom.fr/oai/odroid-linux-3.10.y-rt/commit/4c13dd3b48fcb6fbe44f241eb11a057ecd1cba75
一个实例分析
注
:本文只代表作者个人观点,与任何组织机构无关。
进一步交流
技术
,
可以
加我的
QQ/
微信:
490834312
。如果您想在这个公众号上分享自己的技术干货,也欢迎联系我:)
感谢您的阅读和支持!《企业存储技术》微信公众号:
huangliang_storage
长按二维码可直接识别关注
历
史文章汇总
(传送门):
http://chuansong.me/account/huangliang_storage