[原创]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这些参数),但是当内存的分配占用率高于释放率时,就引起了上述的问题即看到内存一直在增长。

目录
相关文章
|
7月前
|
Linux
Linux rsyslog占用内存CPU过高解决办法
该文档描述了`rsyslog`占用内存过高的问题及其解决方案。
304 4
|
7月前
|
缓存 移动开发 关系型数据库
Linux 内存 占用较高问题排查
Linux 内存 占用较高问题排查
148 2
|
23天前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
145 6
|
22天前
|
缓存 Java Linux
如何解决 Linux 系统中内存使用量耗尽的问题?
如何解决 Linux 系统中内存使用量耗尽的问题?
106 48
|
7月前
|
Java Linux Arthas
linux上如何排查JVM内存过高?
linux上如何排查JVM内存过高?
1092 0
|
7月前
|
监控 Linux
【专栏】在 Linux 中,掌握检查内存使用情况至关重要,因为内存问题可能导致系统性能下降甚至崩溃。这 5 个命令堪称绝了!
【4月更文挑战第28天】在 Linux 中,掌握检查内存使用情况至关重要,因为内存问题可能导致系统性能下降甚至崩溃。本文介绍了 5 个常用的检查内存命令:1) `free` 提供内存和交换区的详细信息;2) `top` 显示进程信息及内存使用;3) `vmstat` 输出系统综合信息,包括内存动态变化;4) `pidstat` 监控特定进程的内存使用;5) `/proc/meminfo` 文件提供系统内存详细数据。了解和使用这些命令能帮助用户及时发现并解决内存相关问题,确保系统稳定运行。
84 0
|
7月前
|
Linux
linux杀死CPU占用过高内存占用过高定时清理
linux杀死CPU占用过高内存占用过高定时清理
62 0
|
7月前
|
存储 缓存 监控
探秘Linux系统内存问题:主体 进程RSS均正常但系统内存下降的调查方法
探秘Linux系统内存问题:主体 进程RSS均正常但系统内存下降的调查方法
167 0
|
存储 缓存 监控
如何检查 Linux 内存使用量是否耗尽?这5个命令堪称绝了!
如何检查 Linux 内存使用量是否耗尽?这5个命令堪称绝了!
238 0
JProfiler分析CPU占用实用教程
JProfiler分析CPU占用实用教程
283 0
JProfiler分析CPU占用实用教程