Slab,小对象也能搞出大事情

排查内存使用(泄露、耗尽)问题的一个的技巧是,区分“批发商”和“零售商”这两类不一样的内存管理机制。这里的“批发商”,指的是按页面管理并分配内存的机制。而“零售商”,则是指从“批发商”那里批量获取资源,并以字节为单位,管理和分配内存的机制。服务器

“零售商”和“批发商”的区分很重要。这是由于经过“零售商”分配出去的内存资源,在“批发商”那里或多或少都有统计。可是从“批发商”那边分配出去的内存资源,“零售商”几乎一无所知。凡是一些诡异的,“个人内存去哪里了”的问题,每每都跟这个有点有关系。数据结构

“批发商”“零售商”的例子不少,好比Windows上的一对接口VirtualAlloc和HeapAlloc。而在Linux内核中,buddy system毋庸置疑是最大内存的“批发商”,而slab则是最经常使用的“零售商”。今天这篇文章,我跟你们分享一个和“零售商”slab有关的真实案例。工具

云监控是一个好产品

今天的案例,咱们从云监控提及。云监控做为一个监控工具,给客户提供了很是丰富的功能。阿里云的客户可使用云监控来随时了解云服务的状态,并且在云服务出现异常的时候,客户能够及时地收到来自云监控的报警信息。阿里云

作为阿里云技术支持的同窗,能够说咱们对云监控有着一种很是“特殊”的感情。这是由于,不少时候客户提给咱们的问题,会以一个云监控的截图开始,看到云监控的截图,那基本就等于咱们有新的问题须要处理了。spa

clipboard.png

以上的截图是云监控经过钉钉消息发送给客户的报警信息。从这个截图里来看,客户的一台ECS实例内存使用率超过了90%,并且持续了1分钟时间。这对ECS服务器来讲,是至关不健康的状态。操作系统

基本套路

处理内存使用问题,有一些基本套路。首先,咱们可使用free或top命令看一下系统整体内存使用状况,如物理内存大小,有多少剩余内存,有多少被cache(这里的cache和slab机制的cache是两回事)和buff用掉了;其次,咱们可使用ps命令看看每一个进程内存的使用状况;若是到这一步尚未定位到问题,进一步,咱们能够经过查看/proc/meminfo文件,在更细的粒度上了解内存资源的分布与分配状况。按照这个套路打下来,通常咱们都会获得一个初步的排查结果。以今天这个问题为例,系统32G内存几乎被用光了,可是cache和buff并不大,进程使用的内存也很少。最终在meminfo文件中,我发现有大量内存被slab用掉了。3d

零售商slab

Slab应该算是Linux内存管理机制中,最著名的“零售商”机制。写slab的文章很是多,我这里只作简单的介绍。对象

作为“零售商”,slab确定须要解决两个问题,一个是怎么样从“批发商”buddy system那里“批发”内存,另一个是怎么样管理并把这些内存“散卖”出去。为了回答这两个问题,咱们首先理解一下slab机制的数据结构。Slab机制包括三个层次的数据结构:cache,slab和object。以下图,一个slab通常是一个4K大小的内存页面。当咱们把slab页面,按照固定大小切分红可以被“零售”的小块的时候,咱们就获得了不少的object。当把包含相同大小object的slab用“箭头”串起来的时候,就造成了cache。这里slab和object是比较容易理解的:由于把4K的页面分配给几个字节的对象使用太浪费内存了,因此就须要切开来“散卖”。而cache的存在乎义实际上是比较隐晦的。Cache存在的意义,实际上是为了动态的管理slab,让slab能够动态的增长和减小。在object需求量大的时候,能够从“批发商”那里拿到不少的slab放进cache里,而在用完以后,又能够动态地把slab退回去。blog

基于cache, slab和object数据结构,slab机制要解决上边那两个问题就变得很容易了:一方面,slab机制从“批发商”buddy system那里,以slab(页面)为单位获取内存;另一方面,slab机制以object为基本管理单元,把内存资源“零售”给系统中其余模块。接口

clipboard.png

Slabtop是一个能够用来查看slab内存使用状况的工具。下边这个截图来自客户问题现场。咱们能够根据cache size这一列,快速地定位到问题。很明显cache size最大的一行对应的对象是dentry。

clipboard.png

注意:为了解释简单,这里我假设slab是一个页面,也就是4K大小。这个假设意味着,全部被管理的对象都是小于4K的。但在实际状况中,为了提供大于4K的内存块,一个slab其实能够是多个连续的物理页面。

小对象搞出大事情

目录项dentry算是一个很是小的内核数据结构。从上边的截图咱们能够看到,dentry其中只有0.19K。虽然dentry很小,可是这个结构是内核中最常使用的数据结构。以下图,当进程打开文件的时候,通常都会有对应的dentry被分配出来。在Linux这样的“一切皆文件”的操做系统里,dentry结构被使用频率是不言自明的。

clipboard.png

由于已经定位到了内核数据结构dentry,因此我选择抓一个dump来慢慢分析这个问题。抓了dump以后让客户重启机器释放内存,以避免致使更严重的后果,影响业务。

Core dump掘金

使用core dump排查问题有两个优点:一是在生产环境中,客户有时候真的很难给咱们机会去使用一些trace和工具一步一步的排查问题,而抓dump对业务的影响较小,抓完以后能够慢慢分析。二是core dump里包含了系统中全部正常、不正常的状态。数据量大且完整,这不是通常的数据收集方法能够比的。下边我演示一遍怎么用core dump来排查slab泄露问题。

系统基本信息

使用sys命令,咱们能够快速看一下客户这台机器的基本信息。如机器名,内核版本,内存大小等。

clipboard.png

内存概要

由于这个问题是内存使用问题,咱们能够用kmem -i命令在dump里看一下内存的使用状况。咱们能够看到,这个系统有32G内存,其中slab用掉27G。

clipboard.png

Cache

接下来咱们看一下slab的使用状况。命令kmem -s能够帮咱们列出内核中全部的slab cache。以下图,每一行对应一个cache,每一个cache负责管理一个对象,第二列是对象的名字。

clipboard.png

Slab

既然已经发现了问题是由dentry对象引发的,那么,咱们能够到slab结构内部去查看泄露的的dentry。命令kmem -S dentry能够帮咱们列出包含dentry的全部slab。能够对照“零售商slab”一节第一个插图来看下边的截图。前两行给出了cache的一些基本信息,如地址,object名字,object大小等。从第三行开始,这个命令分块输出cache中全部的slab结构。每一个slab里包含一列小对象,这些对象都在一个页面内部,从他们连续的地址能够看出这点。实际上这个系统分配了7百万slab页面给dentry,下图只截了前两个。

clipboard.png

小对象dentry

这个时候问题来了。这个cache里有7百万slab,有1亿4千万dentry项,咱们怎么去分析这些项呢?其实有一个很简单的技巧,就是随机挑选的一些dentry,而后看看这些dentry项里,有没有什么共同的特征,好比相同的字符串,或者共同引用的地址等。对这个问题来讲,我是很幸运的。我发如今随机挑选的dentry结构里,d_iname这个字符串都包含一个子串“dOeSnotExist_.db”。

clipboard.png

使用dOeSnotExist_.db这个子串,咱们能够在公网上垂手可得地定位到下边这个问题:这是nss-softokn软件包的一个Bug。

clipboard.png

后记

技术支持工做的一个创新点,也是这个工种的一大乐趣,是能够利用各类组合技,利用不一样的方法,巧妙地组合并找出问题的答案。以今天这个问题为例,咱们开始使用了一些基础的排查工具对问题进行了初步的定位;进而咱们使用了core dump这种看起来不那么易用的工具,从内存中挖出了一个字符串做为线索;最后结合公网上的信息,咱们拼出了这个问题的答案。

本文做者:声东
阅读原文本文为云栖社区原创内容,未经容许不得转载。

相关文章
相关标签/搜索