vm内核参数之缓存回收vfs_cache_pressure
注:本文分析基于3.10.0-693.el7内核版本,即CentOS 7.41、背景在上篇文章中,我们知道在使用drop_caches接口释放内存时,drop_slab中会调用各个slab管理区的shrink函数释放空闲内存。在实际释放前会先计算可释放的空闲slab缓存数量,这个计算在针对超级块以及mbcache时会受vfs_cache_pressure参数控制。2、super block shr
注:本文分析基于3.10.0-693.el7内核版本,即CentOS 7.4
1、背景
在之前的文章《vm内核参数之缓存回收drop_caches》中,我们知道在使用drop_caches接口释放内存时,drop_slab中会调用各个slab管理区的shrink函数释放空闲内存。在实际释放前会先计算可释放的空闲slab缓存数量,这个计算在针对超级块以及mbcache时会受vfs_cache_pressure参数控制。
2、super block shrink
文件系统在初始化时会调用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;
}
3、prune_super
每个超级块都会注册一个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时,也就是要回收多余当前系统缓存量,这样就需要持续一段时间,一边等待系统生成缓存,一边再回收,直到达到指定值。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)