Slab allocator是Linux kernel的内存分配机制,各内核子系统、模块、驱动程序都可以使用,但用完应该记得释放,忘记释放就会造成“内存泄露”(memory leak)。如果导致泄露的代码使用率很低倒也罢了,若是使用率很高的话,系统的内存会被迅速耗尽。

在以下案例中,132 GB 内存,仅剩21 GB空闲,还有16 GB的交换区被用掉了,显然内存使用相当紧张,而内存的主要去向是slab,slab用了89 GB,其中不可回收的部分(SUnreclaim)就占了88 GB:

$ less /proc/meminfo

MemTotal: 132124108 kB

MemFree: 21361420 kB

...

SwapTotal: 33554428 kB

SwapFree: 17019448 kB

...

Slab: 89078888 kB

SReclaimable: 515108 kB

SUnreclaim: 88563780 kB

...

1

2

3

4

5

6

7

8

9

10

11

$less/proc/meminfo

MemTotal:132124108kB

MemFree:21361420kB

...

SwapTotal:33554428kB

SwapFree:17019448kB

...

Slab:89078888kB

SReclaimable:515108kB

SUnreclaim:88563780kB

...

再细看/proc/slabinfo,发现“size-4096”占用了84 GB之多(21720567*4096):

$ less /proc/slabinfo

slabinfo - version: 2.1

# name : tunables : slabdata

...

size-4096 21720555 21720567 4096 1 1 : tunables 24 12 8 : slabdata 21720555 21720567 0

...

1

2

3

4

5

6

$less/proc/slabinfo

slabinfo-version:2.1

# name             : tunables : slabdata

...

size-40962172055521720567409611:tunables24128:slabdata21720555217205670

...

通常Slab的名字就表明了其用途,比如”inode_cache”、”dentry”什么的,根据发生泄露的slab cache的名字,大致就知道是哪个子系统或模块的问题,然而本例比较复杂,因为从”size-4096″的名称完全看不出slab cache的用途。

即便从名字知道了是哪个子系统的问题,为了进一步定位故障点,我们还要看到具体是哪些函数、哪些代码在分配内存才行。要如何诊断此类问题呢?

首先取决于kernel用的是slab还是slub,slab其实是一个统称,Linux kernel自2.6.23之后就已经从Slab进化成Slub了,它自带原生的诊断功能,比Slab更方便。判断kernel是否在使用slub有一个简单的方法,就是看/sys/kernel/slab目录是否存在,如果存在的话就是slub,否则就是slab。SLUB的故障诊断请参考以下文章:如何诊断SLUB问题

SLAB

如果是slab的话,有两种常见方法:一是利用debug kernel的slab leak辅助功能,二是利用systemtap等工具。参见https://access.redhat.com/solutions/358933

使用kernel的DEBUG_SLAB_LEAK功能

这需要kernel编译的时候打开了”CONFIG_DEBUG_SLAB_LEAK”选项才行,默认是没打开的。

对RHEL或CentOS来说,debug kernel打开了此编译选项,可以安装名为kernel-debug-*的rpm软件包,然后重启系统并选择此debug kernel即可。

完成后/proc目录下会出现一个名为slab_allocators的文件,里面会记录类似如下的slab分配的信息,注意观察是什么代码在分配slab,有助于找到可疑的泄漏点。缺点是只记录了直接调用的函数,没有完整的backtrace:

buffer_head: 2555 alloc_buffer_head+0x20/0x75

mm_struct: 9 mm_alloc+0x1e/0x42

mm_struct: 20 dup_mm+0x36/0x370

vm_area_struct: 384 dup_mm+0x18f/0x370

vm_area_struct: 151 do_mmap_pgoff+0x2e0/0x7c3

fs_cache: 8 copy_fs_struct+0x21/0x133

fs_cache: 29 copy_process+0xf38/0x10e3

files_cache: 30 alloc_files+0x1b/0xcf

signal_cache: 81 copy_process+0xbaa/0x10e3

sighand_cache: 77 copy_process+0xe65/0x10e3

anon_vma: 241 anon_vma_prepare+0xd9/0xf3

size-2048: 1 add_sect_attrs+0x5f/0x145

size-2048: 2 journal_init_revoke+0x99/0x302

1

2

3

4

5

6

7

8

9

10

11

12

13

buffer_head:2555alloc_buffer_head+0x20/0x75

mm_struct:9mm_alloc+0x1e/0x42

mm_struct:20dup_mm+0x36/0x370

vm_area_struct:384dup_mm+0x18f/0x370

vm_area_struct:151do_mmap_pgoff+0x2e0/0x7c3

fs_cache:8copy_fs_struct+0x21/0x133

fs_cache:29copy_process+0xf38/0x10e3

files_cache:30alloc_files+0x1b/0xcf

signal_cache:81copy_process+0xbaa/0x10e3

sighand_cache:77copy_process+0xe65/0x10e3

anon_vma:241anon_vma_prepare+0xd9/0xf3

size-2048:1add_sect_attrs+0x5f/0x145

size-2048:2journal_init_revoke+0x99/0x302

使用systemtap

除了使用debug kernel之外,还有个方法就是用systemtap,对内核适当的位置植入探针,有助于找到可疑的slab分配,这需要对内核有一定的了解才行。

普通的slab cache是通过kmem_cache_alloc来分配的,可以用现成的systemtap probe vm.kmem_cache_alloc进行观测。但是在本例中不适用,因为本例中”size-4096″属于slab里的general purpose cache,是供kmalloc()使用的,所以systemtap应该针对kmalloc()进行探测,这里有一个现成的脚本 “kmalloc-top“,它的原理是对__kmalloc()下探针,记录backtraces,因为__kmalloc是实现kmalloc()的核心函数,有的代码会直接调用__kmalloc,所以探测它而不是kmalloc()才不会有遗漏。以上的脚本没有记录kmalloc的size,所以我修改了一下,加上了kmalloc size,修改过的内容如下:

#The systemtap script that instruments the kmalloc

$script="

global kmalloc_stack

probe kernel.function(\"__kmalloc\") { kmalloc_stack[\$size, backtrace()]++ }

probe timer.ms(100), end

{

foreach ([size, stack] in kmalloc_stack) {

printf(\"\\n\")

printf(\" kmalloc size %d\\n\", size)

print_syms(stack)

printf(\"\\n\")

printf(\"%d\\n\", kmalloc_stack[size, stack])

}

delete kmalloc_stack

}

";

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

#The systemtap script that instruments the kmalloc

$script="

global kmalloc_stack

probe kernel.function(\"__kmalloc\") { kmalloc_stack[\$size, backtrace()]++ }

probe timer.ms(100), end

{

foreach ([size, stack] in kmalloc_stack) {

printf(\"\\n\")

printf(\" kmalloc size %d\\n\", size)

print_syms(stack)

printf(\"\\n\")

printf(\"%d\\n\", kmalloc_stack[size, stack])

}

delete kmalloc_stack

}

";

以root身份执行:

# ./kmalloc-top -o '--all-modules' > /tmp/kmtop.out

1

# ./kmalloc-top -o '--all-modules' > /tmp/kmtop.out

间隔一段时间再ctrl-c退出,看到结果如下:

...

This path seen 1021 times:

kmalloc size 4096

0xffffffff811783e0 : __kmalloc+0x0/0x230 [kernel]

0xffffffffa022401e : 0xffffffffa022401e [sisips]

0xffffffffa024d46f : 0xffffffffa024d46f [sisips]

0xffffffffa023b763 : 0xffffffffa023b763 [sisips]

0xffffffffa022abca : 0xffffffffa022abca [sisips]

0xffffffffa022d51a : 0xffffffffa022d51a [sisips]

0xffffffff81290745 : _atomic_dec_and_lock+0x55/0x80 [kernel]

0xffffffff81193611 : __fput+0x1a1/0x210 [kernel]

0xffffffff810e884e : __audit_syscall_exit+0x25e/0x290 [kernel]

0xffffffff8100b0d2 : system_call_fastpath+0x16/0x1b [kernel]

This path seen 1021 times:

kmalloc size 4096

0xffffffff811783e0 : __kmalloc+0x0/0x230 [kernel]

0xffffffffa022401e : 0xffffffffa022401e [sisips]

0xffffffffa0224a32 : 0xffffffffa0224a32 [sisips]

0xffffffffa022abac : 0xffffffffa022abac [sisips]

0xffffffffa024f5c8 : 0xffffffffa024f5c8 [sisips]

0xffffffffa022d51a : 0xffffffffa022d51a [sisips]

0xffffffff81290745 : _atomic_dec_and_lock+0x55/0x80 [kernel]

0xffffffff81193611 : __fput+0x1a1/0x210 [kernel]

0xffffffff810e884e : __audit_syscall_exit+0x25e/0x290 [kernel]

0xffffffff8100b0d2 : system_call_fastpath+0x16/0x1b [kernel]

This path seen 853 times:

kmalloc size 4096

0xffffffff811783e0 : __kmalloc+0x0/0x230 [kernel]

0xffffffffa022401e : 0xffffffffa022401e [sisips]

0xffffffffa0222c30 : 0xffffffffa0222c30 [sisips]

0xffffffffa024d46f : 0xffffffffa024d46f [sisips]

0xffffffffa024dd49 : 0xffffffffa024dd49 [sisips]

0xffffffff81178001 : s_show+0x2c1/0x330 [kernel]

0xffffffffa02240bc : 0xffffffffa02240bc [sisips]

0xffffffffa023b783 : 0xffffffffa023b783 [sisips]

0xffffffffa022abca : 0xffffffffa022abca [sisips]

0xffffffffa022d51a : 0xffffffffa022d51a [sisips]

0xffffffff81290745 : _atomic_dec_and_lock+0x55/0x80 [kernel]

0xffffffff81193611 : __fput+0x1a1/0x210 [kernel]

0xffffffff810e884e : __audit_syscall_exit+0x25e/0x290 [kernel]

0xffffffff8100b0d2 : system_call_fastpath+0x16/0x1b [kernel]

...

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

...

Thispathseen1021times:

kmallocsize4096

0xffffffff811783e0:__kmalloc+0x0/0x230[kernel]

0xffffffffa022401e:0xffffffffa022401e[sisips]

0xffffffffa024d46f:0xffffffffa024d46f[sisips]

0xffffffffa023b763:0xffffffffa023b763[sisips]

0xffffffffa022abca:0xffffffffa022abca[sisips]

0xffffffffa022d51a:0xffffffffa022d51a[sisips]

0xffffffff81290745:_atomic_dec_and_lock+0x55/0x80[kernel]

0xffffffff81193611:__fput+0x1a1/0x210[kernel]

0xffffffff810e884e:__audit_syscall_exit+0x25e/0x290[kernel]

0xffffffff8100b0d2:system_call_fastpath+0x16/0x1b[kernel]

Thispathseen1021times:

kmallocsize4096

0xffffffff811783e0:__kmalloc+0x0/0x230[kernel]

0xffffffffa022401e:0xffffffffa022401e[sisips]

0xffffffffa0224a32:0xffffffffa0224a32[sisips]

0xffffffffa022abac:0xffffffffa022abac[sisips]

0xffffffffa024f5c8:0xffffffffa024f5c8[sisips]

0xffffffffa022d51a:0xffffffffa022d51a[sisips]

0xffffffff81290745:_atomic_dec_and_lock+0x55/0x80[kernel]

0xffffffff81193611:__fput+0x1a1/0x210[kernel]

0xffffffff810e884e:__audit_syscall_exit+0x25e/0x290[kernel]

0xffffffff8100b0d2:system_call_fastpath+0x16/0x1b[kernel]

Thispathseen853times:

kmallocsize4096

0xffffffff811783e0:__kmalloc+0x0/0x230[kernel]

0xffffffffa022401e:0xffffffffa022401e[sisips]

0xffffffffa0222c30:0xffffffffa0222c30[sisips]

0xffffffffa024d46f:0xffffffffa024d46f[sisips]

0xffffffffa024dd49:0xffffffffa024dd49[sisips]

0xffffffff81178001:s_show+0x2c1/0x330[kernel]

0xffffffffa02240bc:0xffffffffa02240bc[sisips]

0xffffffffa023b783:0xffffffffa023b783[sisips]

0xffffffffa022abca:0xffffffffa022abca[sisips]

0xffffffffa022d51a:0xffffffffa022d51a[sisips]

0xffffffff81290745:_atomic_dec_and_lock+0x55/0x80[kernel]

0xffffffff81193611:__fput+0x1a1/0x210[kernel]

0xffffffff810e884e:__audit_syscall_exit+0x25e/0x290[kernel]

0xffffffff8100b0d2:system_call_fastpath+0x16/0x1b[kernel]

...

可以看到,大量的size-4096分配来自内核模块”sisips”,有理由对它产生怀疑。(因为这是Symantec的内核模块,系统上没有它的debuginfo,所以systemtap解析不了它的backtrace符号,只能显示出16进制的地址)。为了验证该模块是否真的导致了内存泄露,可以暂时禁用它,观察/proc/slabinfo看size-4096是否停止疯涨,如果停了,显然该模块就有问题了。

另一种方法:kmemleak

检测内核内存泄漏还有另一种方法,就是利用kmemleak工具,它并不是针对某一个slab,而是针对所有的内核内存。详见:

用KMEMLEAK检测内核内存泄漏

linux slab常见问题,怎样诊断slab泄露问题相关推荐

  1. Linux内存管理中的slab分配器

    Linux内核中基于伙伴算法实现的分区页框分配器适合大块内存的请求,它所分配的内存区是以页框为基本单位的.对于内核中小块连续内存的请求,比如说几个字节或者几百个字节,如果依然分配一个页框来来满足该请求 ...

  2. Linux内存管理之slab机制(创建slab)

    Linux内核中创建slab主要由函数cache_grow()实现,从slab的创建中我们可以完整地看到slab与对象.页面的组织方式. /* * Grow (by 1) the number of ...

  3. Linux内存管理第八章 -- Slab Allocator (二)

    文章目录 Linux内存管理 -- Slab Allocator (二) Slabs Storing the Slab Descriptor Slab Creation Tracking Free O ...

  4. linux slab机制,详解slab机制

    目前有很多讲slab的文章,要么是纯讲原理画一堆图结合源码不深导致理解困难,要么是纯代码注释导致理解更困难,我在猛攻了一周时间后,细致总结一下slab,争取从原理到源码都能细致的理解到并立刻达到清楚的 ...

  5. linux内存分配机制,Linux内存分配机制:SLAB / SLUB / SLOB

    Linux内存分配机制:SLAB / SLUB / SLOB [日期:2011-07-15] 来源:Linux社区 作者:do2jiang [字体:大 中 小] slob: introduce the ...

  6. linux内核之slob、slab、slub

    Table of Contents Slob, Slab VS Slub Overview slub分配器 Linux slab 分配器剖析 动态内存管理 slab 缓存 slab 背后的动机 API ...

  7. linux 系统监控、诊断工具之 IO wait

    1.问题: 最近在做日志的实时同步,上线之前是做过单份线上日志压力测试的,消息队列和客户端.本机都没问题,但是没想到上了第二份日志之后,问题来了: 集群中的某台机器 top 看到负载巨高,集群中的机器 ...

  8. 超全整理 | 嵌入式Linux 性能工具和诊断思路

    作为程序员,和 Linux 打交道,在服务器上分析系统性能情况,我觉得是每一个后端工程师都无法避开的事情. 无论你是开发还是运维,可能都经历过这样的场景: 流量高峰期,服务器 CPU 使用率过高报警, ...

  9. linux下检测硬盘,【转载】linux下硬盘监控诊断工具SmartTools

    对于windwos下raid卡具备告警功能,当硬盘故障.raid卡告警时,可以发邮件给管理员.IBM.HP.Dell都支持.但在linux下,就没有找到相关的好工具了,今天到陈沙克的博客上到一篇关于l ...

最新文章

  1. Maze Problem(求最短距离)BFS
  2. Kubernetes — Harbor 分布式镜像仓库
  3. Python语言学习之字母A开头函数使用集锦:assert用法之详细攻略
  4. 推荐一个有趣的Chrome扩展程序-查看任意网站的开发技术栈
  5. (91)多人投票器(五人投票器)
  6. C语言dev查看调试变量,C语言程序设计-c4_C语言上机_Dev调试.doc
  7. 2021-2025年中国电动毛巾散热器行业市场供需与战略研究报告
  8. IT结合测试时,准备数据的注意事项(之二:表之间的关系)。
  9. [恢]hdu 2000
  10. Confuser.crproj
  11. B站 郝斌C语言 课件+笔记 (全)
  12. uva 10098(全排列)
  13. unity隐藏鼠标光标的2种方法
  14. 「卷无人道」,手机app定制
  15. 自定义firefox背景色(豆绿色)
  16. 文字logo设计的作用
  17. 为什么用样本方差估计总体方差的统计量除以n-1
  18. 百度地图API之GPS坐标转换
  19. python3.9安装wordcloud报错:ERROR: Command errored out with exit status 1: ....
  20. 浅谈数据中台与数据仓库的异同

热门文章

  1. 利用Linux下DDOS工具tfn2k进行DDOS攻击试验
  2. html车牌输入框,车牌号码输入框——Label的inputView
  3. 震惊,双色球秘诀竟然是这样。。。。。。。java预测双色球
  4. html验证邮箱和手机号,js与jquery正则验证电子邮箱、手机号、邮政编码的方法
  5. 重启服务器之后的 502 Bad Gateway
  6. c#父窗体子窗体之间传值
  7. jQuery内置特效
  8. (-0001)java后台开发之学习Java好书及视频推荐
  9. MacBook长期待机导致网页视频无法播放
  10. 深入理解js中实现继承的原理和方法