[原创]systemtap脚本分析系统中dentry SLAB占用过高问题

简介: 利用systemtap脚本分析系统中dentry SLAB占用过高问题

摘要

利用systemtap脚本分析系统中dentry SLAB占用过高问题

原创文章:来自systemtap脚本分析系统中dentry SLAB占用过高问题

背景

长时间运行着的tengine主机有内存占用75%以上的报警.

操作系统版本: 2.6.32.el6.x86_64

原因定位

收集内存使用的相关信息如下:
因内存占用率报警mem:76.28%先看一下内存的总体使用状况,从下图中可以看出used占用较高,buffers/cached占用较少(关于cached占用过高的分析处理请参见另一篇文章), 这时首先想到是不是有进程内存泄漏,经查询tengine和别的进程没有发现有占用大量内存即不存在内存泄漏。
1

cat /proc/meminfo细化查看内存各部分的使用,可以发现slab占用过高约40GB,同时也可以观察到SReclaimable也很高,即SReclaimable指可收回Slab的大小。
2

查看监控中的slab曲线一直处于增长状态
3

详细查看slab中各部分内存的占用,可以看出dentry占用的很多,大约在40GB内存。
4

dentry的作用:当读写文件时内核会为该文件对象建立一个dentry,并将其缓存起来,方便下一次读写时直接从内存中取出提高效率。

5

再详细查看一下dentry的使用情况:
$cat /proc/sys/fs/dentry-state
209815031 209805757 45 0 0 0

从上查看dentry使用数量,可以发现有大量unused dentries占用率很高,没有释放掉。

以上数据的详细含义可以man proc查看,下边是可以从网上查到的中文含义
6

dentunusd:在缓冲目录条目中没有使用的条目数量.
file-nr:被系统使用的文件句柄数量.
inode-nr:使用的索引节点数量.
pty-nr:使用的pty数量.
1)dentunusd
dentunusd数据的数据来源是/proc/sys/fs/dentry-state的第二项数据.
要弄明白它的意义,我们首先要弄明白dcache(目录高速缓存),因为系统中所有的inode都是通过文件名来访问的,而为了解决文件名到inode转换的时间,就引入了dcache.
它是VFS层为当前活动和最近使用的名字维护的一个cache.
dcache中所有处于unused状态和negative(消极)状态的dentry对象都通链入到dentry_unused链表中,这种dentry对象在回收内存时可能会被释放.
如果我们在系统中运行ls -ltR /etc/会看到dentunusd的数量会多起来.
而通过mount -o remount /dev/sda1会看到dentunusd会迅速会回收.
2)file-nr
file-nr的的数据来源是/proc/sys/fs/file-nr文件的第一项数据.
实际上file-nr不是一个准确的值,file-nr每次增加的步长是64(64位系统),例如现在file-nr为2528,实际上可能只打开了2527个文件,而此时你打开两个文件后,它就会变成2592,而不是2530.
3)inode-nr
inode-nr的数据来源是/proc/sys/fs/inode-nr文件的第一项数据减去第二项数据的值.
inode-nr文件的第一项数据是已经分配过的INODE节点.第二项数据是空闲的INODE节点.
例如,inode-nr文件里的值为:13720 7987
我们新建一个文件file1,此时inode-nr第一项数据会加1,就是13721,表示系统里建立了这么多的inode.
我们再删除掉file1,此时就会变成13720.
空闲的INODE节点表示我们已经里这么多的INODE节点曾经有过被利用,但没有被释放.
所以INODE节点总数减去空闲的INODE,就是正在被用的INODE.
最后通过使用mount -o remount /dev/sda1命令,空闲节点会被刷新,所以inode-nr的值会有所变化.
4)pty-nr
pty-nr的数据来源是/proc/sys/kernel/pty/nr
表示登陆过的终端总数,如果我们登录过10回,退出了3回,最后的结果还是10回.

也可用于sar命令查看dentunusd以秒为间隔查看dentry的变化。
$sar -v 1

12:07:09 AM dentunusd file-nr inode-nr pty-nr
12:07:10 AM 183107848 1088 42210 44
12:07:11 AM 183107871 1088 42245 44
12:07:12 AM 183116550 1152 42290 44
12:07:13 AM 183116581 1088 42343 44
12:07:14 AM 183116617 1216 42396 44
12:07:15 AM 183116059 1216 40585 44

关于linux dentry的介绍可以搜出来一大把,包括dentry占用过高,不外乎两种处理方法,一种是直接通过proc系统清理dentry,命令如下:
sudo sh -c "echo 2 > /proc/sys/vm/drop_caches"
缺点是执行命令过程容易hang住几分钟,而且redhat官方也不建议使用这种方式清理cache等;另外一种方法是通过调整内核参数vm.vfs_cache_pressure,含义如下,先调整为vm.vfs_cache_pressure=10000观察一天也没有发现能使dentry降下去。
vm.vfs_cache_pressure控制内核回收用于dentry和inode cache内存的倾向。 默认值是100,内核会根据pagecache和swapcache的回收情况,让dentry和inode cache的内存占用量保持在一个相对公平的百分比上。减小vfs_cache_pressure会让内核更倾向于保留dentry和inode cache。当vfs_cache_pressure等于0,在内存紧张时内核也不会回收dentry和inode cache,这容易导致OOM。如果vfs_cache_pressure的值超过100,内核会更倾向于回收dentry和inode cache。

细节分析

即然上述两个办法都不是完美的方法,就需要确认这么多unused dentries到底是那些文件占用的? 按正常理解情况下linux内核不太可能释放不了dentry。
首先通过查看内核dentry结构部分的代码(fs/dcache.c),搞清楚了所有的unused dentries挂在了super_blocks 结构的s_dentry_lru链表上,可以通过遍历此链表上的节点输出这些节点代表的文件名,就可以知道dentries被那些文件占用。
思路:用systemtap工具做内核探测,将每个节点的文件名写入到log文件。guru模式关键代码如下:

        if (!list_empty(&sb->s_dentry_lru)) {
            list_for_each_entry(dent, &sb->s_dentry_lru, d_lru) {
                spin_lock(&dent->d_lock);
                if (dent->d_flags & DCACHE_REFERENCED) {
                    dcache_referenced_nr++;
                }
                spin_unlock(&dent->d_lock);

                memset(path, 0, 2048); 

                cp = dentry_path_stp(dent, path, 1024); //解析每个dentry的文件路径,参考char *dentry_path(struct dentry *dentry, char *buf, int buflen)的实现
                if (!IS_ERR(cp)) {
                    if(strlen(cp))
                        //offset += snprintf(buf+offset, PAGE_SIZE-offset, "%s", cp);
                        ;
                    else
                        cp = path+1024;                        
                }  
                 
                pp = path+1024;
                pp += sprintf(path+1024, "%d ", dcache_referenced_nr);
                
                if (offset + (pp-cp) + 3 >= PAGE_SIZE) {//\r\n space
                     fp->f_op->write(fp, buf, offset, &fp->f_pos);     //将文件路径写log
                     offset = 0;
                     memset(buf,0,PAGE_SIZE);
                }

                offset += snprintf(buf+offset, PAGE_SIZE-offset, "%s %s\r\n", cp, path+1024);
            }//end list_for

日志分析

查看某一台主机上的dentry条目如下,约3.3亿。
$cat /proc/sys/fs/dentry-state
338031179 338022259 45 0 0 0

systemtap脚本执行完成后,生成日志文件,经分析/etc/pki/nssdb/xxxx,占用量最大1点多亿条dentry.
$sudo grep "/etc/pki/nssdb" dcache.log |wc -l
131071404

$grep '/etc/pki/nssdb' dcache.log | more
/etc/pki/nssdb/._dOeSnotExist_-869749107.db 223
/etc/pki/nssdb/._dOeSnotExist_-869749108.db 224
/etc/pki/nssdb/._dOeSnotExist_-869749109.db 225
/etc/pki/nssdb/._dOeSnotExist_-869749110.db 226
/etc/pki/nssdb/._dOeSnotExist_-869749111.db 227
/etc/pki/nssdb/._dOeSnotExist_-869749112.db 228
/etc/pki/nssdb/._dOeSnotExist_-869749113.db 229
/etc/pki/nssdb/._dOeSnotExist_-869749114.db 230
/etc/pki/nssdb/._dOeSnotExist_-869749115.db 231
/etc/pki/nssdb/._dOeSnotExist_-869749116.db 232
/etc/pki/nssdb/._dOeSnotExist_-869749117.db 233
/etc/pki/nssdb/._dOeSnotExist_-869749118.db 234
/etc/pki/nssdb/._dOeSnotExist_-869749119.db 235
/etc/pki/nssdb/._dOeSnotExist_-869749120.db 236
/etc/pki/nssdb/._dOeSnotExist_-869749121.db 237
/etc/pki/nssdb/._dOeSnotExist_-869749122.db 238
/etc/pki/nssdb/._dOeSnotExist_-869749123.db 239
/etc/pki/nssdb/._dOeSnotExist_-869749124.db 240
/etc/pki/nssdb/._dOeSnotExist_-869749125.db 241
/etc/pki/nssdb/._dOeSnotExist_-869749126.db 242
/etc/pki/nssdb/._dOeSnotExist_-869749127.db 243
/etc/pki/nssdb/._dOeSnotExist_-869749128.db 244
/etc/pki/nssdb/._dOeSnotExist_-869749129.db 245
/etc/pki/nssdb/._dOeSnotExist_-869749130.db 246
……

从上边可能看出只有/etc/pki/nssdb/._dOeSnotExist_ 的使用不明确搜索一把,发现redhat 6系列的系统中存在的问题, bug分析详细参考:
Can curl HTTPS requests make fewer access system calls?
https://bugzilla.redhat.com/show_bug.cgi?id=1044666

因为tengine主机上调用了大量的curl https做探测(curl的版本7.19),使用如下命令做了一下分析可以明确发现一次curl有上百次的访问/etc/pki/nssdb/.xxx文件产生大量的dentry,

sudo strace -f -e trace=access curl 'https://www.taobao.com'

另外由于agent周期性的在拉取配置文件这些配置文件以当前时刻做文件名,也造成大量的dentry。两者加起来的数量基本符合/proc/sys/fs/dentry-state查出来的数量。
至此问题的两个主要原因已经比较清楚。

解决方案

  1. 保留系统和应用程序所需要的可用内存,如调整vm.min_free_kbytes = 4194304 vm.extra_free_kbytes = 4194304 使系统free的内存最少保持在8GB以上(注意6U系统才有extra_free_kbytes)
  2. 升级curl更高版本
  3. 升级nss-softokn 与nss-util rpm包到较高的版本 3.14.3-22,3.16.2.3(未验证参考http://support.huawei.com/huaweiconnect/enterprise/thread-333119.html)
  4. 优化tengine的配置与日志文件使尽可能少的产生文件dentry

案例小结

此例问题对所有linux主机上dentry占用较高分析有借鉴参考意义。

备注
内存回收主要的两大机制:kswapd和“Low on Memory Reclaim”。即当系统中的可用内存很少时,守护进程kswapd被唤醒开始释放页面,如果内存压力很大,进程也会同步地释放内存,这种情况被称为direct-reclaim.
kswapd释放内存页面的数量是有一定的比例的(有兴趣可以查阅pages_min, pages_low and pages_high这些参数),但是当内存的分配占用率高于释放率时,就引起了上述的问题即看到内存一直在增长。

目录
相关文章
|
14天前
|
移动开发 运维 监控
掌握Linux运维利器:查看CPU和内存占用,轻松解决性能问题!
掌握Linux运维利器:查看CPU和内存占用,轻松解决性能问题!
|
21天前
|
缓存 移动开发 关系型数据库
Linux 内存 占用较高问题排查
Linux 内存 占用较高问题排查
30 2
|
19天前
|
Linux
linux杀死CPU占用过高内存占用过高定时清理
linux杀死CPU占用过高内存占用过高定时清理
11 0
|
22天前
|
监控 Linux Shell
Linux 进程问题调查探秘:分析和排查频繁创建进程问题
Linux 进程问题调查探秘:分析和排查频繁创建进程问题
39 0
JProfiler分析CPU占用实用教程
JProfiler分析CPU占用实用教程
232 0
JProfiler分析CPU占用实用教程
|
Arthas Java 测试技术
linux上怎么排查JVM内存过高?
想必工作一两年以后的同学都会逐渐面临到,jvm等问题,但是可能苦于无法熟练的使用一些工具;本文将介绍几个比较常用分析工具的使用方法,带着大家一步步定位分析问题。
linux上怎么排查JVM内存过高?
|
缓存 监控 Linux
Linux 内存和系统性能常用监控管理命令(上)|学习笔记
快速学习 Linux 内存和系统性能常用监控管理命令(上)
128 0
Linux 内存和系统性能常用监控管理命令(上)|学习笔记
|
监控 前端开发 安全
Linux 内存和系统性能常用监控管理命令(下)|学习笔记
快速学习 Linux 内存和系统性能常用监控管理命令(下)
82 0
Linux 内存和系统性能常用监控管理命令(下)|学习笔记
|
存储 缓存 监控
关于Linux中使用USE(使用率/饱和度/错误)方法分析系统性能的一些笔记
如果说希望通过`USE`做一些调优的工作,我觉得需要一定的能力,但是可以通过`USE`来定位机器的性能瓶颈,做一些排故工作。比如机器上的应用发生某些已知的未知故障,比如客户感知卡顿,工单流转,服务编排,调度任务等特别慢的情况,希望确认是机器性能问题,还是应用程序问题,这个时候,使用`USE`方法是一个很好的策略。
364 0
关于Linux中使用USE(使用率/饱和度/错误)方法分析系统性能的一些笔记
|
并行计算
ImageMagick内存占用过高被杀掉
ImageMagick内存占用过高被杀掉
215 0