读写文件频繁的环境经常碰到大量dentry占用内存高的情况,本文介绍一种通过crash找到这些dentry
对应的文件的方法。
通过cat /proc/sys/fs/dentry-state的信息可以看到正在运行系统的dentry数量:
# cat /proc/sys/fs/dentry-state
那么我们来看下内核是怎么统计dentry数量的。
从代码中可以看到unused状态的dentry成员dentry->d_lru是通过struct super_block 的struct list_head s_dentry_lru连接起来的:
代码语言:javascript复制/*
* dentry_lru_(add|del|prune|move_tail) must be called with d_lock held.
*/
static void dentry_lru_add(struct dentry *dentry)
{
if (unlikely(!(dentry->d_flags & DCACHE_LRU_LIST))) {
spin_lock(&dcache_lru_lock);
dentry->d_flags |= DCACHE_LRU_LIST;
list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
dentry->d_sb->s_nr_dentry_unused ;
dentry_stat.nr_unused ;
spin_unlock(&dcache_lru_lock);
}
}
struct super_block {
....
struct list_head s_dentry_lru; /* unused dentry lru */
...
}
struct dentry {
...
struct list_head d_lru; /* LRU list */
...
};
根据以上信息就可以通过coredump找到所有的dentry信息了:
1、获取所有struct super_block的地址信息:
mount >/opt/mount.txt
cat mount.txt | grep -v SUPERBLK | awk '{print $2}' |sort -u > list_mount.txt
2、根据list super_block.s_dentry_lru -h ffff882fb855a000可以获取地址为ffff882fb855a000的super_block成员s_dentry_lru连接起来的所有dentry的成员d_lru,将文件list_mount.txt内容修改如下以获取所有文件系统对应的各自所有dentry.d_lru
vi list_mount.txt后在vi模块下执行如下命令在地址前添加字符串"list super_block.s_dentry_lru -h "
:%s/^/list super_block.s_dentry_lru -h /
注:ffff882fb855a000为super_block地址
3, 执行获取所有文件系统的dentry成员dentry->d_lru
< /opt/dump/list_mount.txt > /opt/dump/list_dentry_lru.txt
4,d_lru地址相对struct dentry的偏移0x88,因此将list_dentry_lru.txt文件中d_lru地址减去0x88就获得所有struct dentry的地址:
for i in `cat list_dentry_lru.txt | grep -v list` ;do printf 0x%x $((0x$i - 0x88));echo "";done > list_dentry.txt
5,修改list_dentry.txt文件得到crash指令获取dentry对应的文件名:
vi编辑list_dentry.txt在地址前添加字符串"struct dentry.d_iname "
:%s/^/struct dentry.d_iname /
6,执行如下命令完成后文件名被保存到list_file.txt中:
< /opt/dump/list_dentry.txt > /opt/dump/list_file.txt
参考:
https://patchwork.kernel.org/patch/9884869/
https://lkml.org/lkml/2014/5/28/566
https://access.redhat.com/solutions/4423151