注:本文分析基于3.10.0-693.el7内核版本,即CentOS 7.4
在之前的文章《vm内核参数之缓存回收drop_caches》中,我们知道在使用drop_caches接口释放内存时,drop_slab中会调用各个slab管理区的shrink函数释放空闲内存。在实际释放前会先计算可释放的空闲slab缓存数量,这个计算在针对超级块以及mbcache时会受vfs_cache_pressure参数控制。
文件系统在初始化时会调用alloc_super分配超级块,此时就会设置超级块的shrink函数,为prune_super。
static struct super_block *alloc_super(struct file_system_type *type, int flags) { struct super_block *s = kzalloc(sizeof(struct super_block_wrapper), GFP_USER); ... INIT_LIST_HEAD(&s->s_inodes); //s_inodes链表存放该超级块所有inode INIT_LIST_HEAD(&s->s_dentry_lru); //s_dentry_lru链表存放未使用的dentry INIT_LIST_HEAD(&s->s_inode_lru); //s_inode_lru链表存放未使用的inode ... s->s_shrink.seeks = DEFAULT_SEEKS; // DEFAULT_SEEKS值为2 s->s_shrink.shrink = prune_super; // 每个超级块对应的shrink函数 s->s_shrink.batch = 1024; //批处理数量 return s; fail: destroy_super(s); return NULL; }每个超级块都会注册一个shrink函数,当内存不足时用于回收内存,主要就是回收未使用的dentry和inode缓存,
static int prune_super(struct shrinker *shrink, struct shrink_control *sc) { struct super_block *sb; int fs_objects = 0; int total_objects; sb = container_of(shrink, struct super_block, s_shrink); /* * Deadlock avoidance. We may hold various FS locks, and we don't want * to recurse into the FS that called us in clear_inode() and friends.. */ if (sc->nr_to_scan && !(sc->gfp_mask & __GFP_FS)) return -1; if (sb->s_op && sb->s_op->nr_cached_objects) fs_objects = sb->s_op->nr_cached_objects(sb); total_objects = sb->s_nr_dentry_unused + sb->s_nr_inodes_unused + fs_objects + 1; //batch size if (sc->nr_to_scan) { int dentries; int inodes; /* proportion the scan between the caches */ //按照比例分配dentry和inode的扫描额度 dentries = (sc->nr_to_scan * sb->s_nr_dentry_unused) / total_objects; inodes = (sc->nr_to_scan * sb->s_nr_inodes_unused) / total_objects; if (fs_objects) fs_objects = (sc->nr_to_scan * fs_objects) / total_objects; /* * prune the dcache first as the icache is pinned by it, then * prune the icache, followed by the filesystem specific caches */ //先回收dentry缓存,扫描该超级块上的s_dentry_lru链表 prune_dcache_sb(sb, dentries); //再回收inode缓存,扫描该超级块上的s_inode_lru链表 prune_icache_sb(sb, inodes); if (fs_objects && sb->s_op->free_cached_objects) { sb->s_op->free_cached_objects(sb, fs_objects); fs_objects = sb->s_op->nr_cached_objects(sb); } //再次计算此时系统中可回收的slab缓存数量 total_objects = sb->s_nr_dentry_unused + sb->s_nr_inodes_unused + fs_objects; } //通过vfs_cache_pressure控制返回的空闲缓存数量,默认值100,即系统存在多少就返回多少 //增加该值就会让shrink函数扫描和释放更多的缓存,相反,减少该值时,就不会回收那么多内存 total_objects = (total_objects / 100) * sysctl_vfs_cache_pressure; return total_objects; }由上可以,vfs_cache_pressure通过控制缓存回收时扫描和回收的数量来达到释放缓存的多少,系统默认值是100,也就是默认释放当前系统所有缓存,当值大于100时,也就是要回收多余当前系统缓存量,这样就需要持续一段时间,一边等待系统生成缓存,一边再回收,直到达到指定值。