[转帖]vm内核参数之缓存回收drop_caches
注:本文分析基于3.10.0-693.el7内核版本,即CentOS 7.4
1、关于drop_caches
通常在内存不足时,我们习惯通过echo 3 > /proc/sys/vm/drop_caches 的方式手动清理系统缓存,
[root@localhost ~]# free -m
total used free shared buff/cache available
Mem: 7822 3436 2068 40 2317 3997
Swap: 0 0 0
[root@localhost ~]# echo 3 > /proc/sys/vm/drop_caches
[root@localhost ~]# free -m
total used free shared buff/cache available
Mem: 7822 3433 4036 40 352 4037
Swap: 0 0 0
对于数字3的含义,我们可以通过内核文档了解其具体含义,
To free pagecache:
echo 1 > /proc/sys/vm/drop_caches
To free reclaimable slab objects (includes dentries and inodes):
echo 2 > /proc/sys/vm/drop_caches
To free slab objects and pagecache:
echo 3 > /proc/sys/vm/drop_caches
2、释放pagecache
在之前我们知道当内存低于某个阈值时,会触发脏页回写,提交回写work到对应BDI设备上,由BDI writebacke进程回写脏页释放内存。这和drop_caches中的echo 1类似,都是释放脏页,因此其最后路径是一致的。
int drop_caches_sysctl_handler(ctl_table *table, int write,
void __user *buffer, size_t *length, loff_t *ppos)
{
int ret;
ret <span class="token operator">=</span> <span class="token function">proc_dointvec_minmax</span><span class="token punctuation">(</span>table<span class="token punctuation">,</span> write<span class="token punctuation">,</span> buffer<span class="token punctuation">,</span> length<span class="token punctuation">,</span> ppos<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>ret<span class="token punctuation">)</span>
<span class="token keyword">return</span> ret<span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>write<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">static</span> <span class="token keyword">int</span> stfu<span class="token punctuation">;</span>
<span class="token comment">// echo 1 > drop_caches</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>sysctl_drop_caches <span class="token operator">&</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token function">iterate_supers</span><span class="token punctuation">(</span>drop_pagecache_sb<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">count_vm_event</span><span class="token punctuation">(</span>DROP_PAGECACHE<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// echo 2 > drop_caches</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>sysctl_drop_caches <span class="token operator">&</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token function">drop_slab</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">count_vm_event</span><span class="token punctuation">(</span>DROP_SLAB<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>stfu<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token function">pr_info</span><span class="token punctuation">(</span><span class="token string">"%s (%d): drop_caches: %d\n"</span><span class="token punctuation">,</span>
current<span class="token operator">-></span>comm<span class="token punctuation">,</span> <span class="token function">task_pid_nr</span><span class="token punctuation">(</span>current<span class="token punctuation">)</span><span class="token punctuation">,</span>
sysctl_drop_caches<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">//置位,否则就一直在回收了</span>
stfu <span class="token operator">|</span><span class="token operator">=</span> sysctl_drop_caches <span class="token operator">&</span> <span class="token number">4</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
可见,echo 1时,会调用drop_pagecache_sb去释放pagecache,我们继续往下查,
drop_pagecache_sb ->
iput ->
iput_final->
write_inode_now -> #提交writeback_control,立即回写
writeback_single_inode ->
__writeback_single_inode ->
do_writepages #调用对应文件系统的writepage写回磁盘
在BDI回写里,一开始提交的是wb_writeback_work,等到实际要执行回写操作时,都会转换为writeback_control,再去执行回写。
因此,echo 1的操作就是,遍历每个超级块,调用drop_pagecache_sb,drop_pagecache_sb中会遍历该超级块所有的inode,对其关联的pagecache进行回写。与BDI不同的是,该操作是立马执行,不需要等待周期执行或者inode过期。
3、释放slab cache
而对于echo 2的情况,就比较复杂一点,
static void drop_slab(void)
{
int nr_objects;
struct shrink_control shrink = {
.gfp_mask = GFP_KERNEL,
};
//上次回收缓存数量高于10,就再进行一次回收
//这个条件其实挺苛刻的,回收后整个系统空闲slab不会超过10
do {
nr_objects = shrink_slab(&shrink, 1000, 1000);
} while (nr_objects > 10);
}
unsigned long shrink_slab(struct shrink_control shrink,
unsigned long nr_pages_scanned,
unsigned long lru_pages)
{
struct shrinker shrinker;
unsigned long ret = 0;
...
//遍历系统中所有的shrinker,回收各个slab管理区的空闲缓存
list_for_each_entry(shrinker, &shrinker_list, list) {
unsigned long long delta;
long total_scan;
long max_pass;
int shrink_ret = 0;
long nr;
long new_nr;
//获取批处理数量,默认每次回收128,对于超级块而言是1024
long batch_size = shrinker->batch ? shrinker->batch
: SHRINK_BATCH;
//获取该slab管理区可回收的缓存数量
max_pass = do_shrinker_shrink(shrinker, shrink, 0);
if (max_pass <= 0)
continue;
nr <span class="token operator">=</span> <span class="token function">atomic_long_xchg</span><span class="token punctuation">(</span><span class="token operator">&</span>shrinker<span class="token operator">-></span>nr_in_batch<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
total_scan <span class="token operator">=</span> nr<span class="token punctuation">;</span>
<span class="token comment">//计算该slab管理区此次缓存回收额度,一堆操作</span>
<span class="token comment">//针对手动释放缓存的场景,基本上是两倍的max_pass,也就是尽可能去释放</span>
<span class="token comment">//对于kswap或其他路径上,不会超过一倍的max_pass</span>
delta <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">4</span> <span class="token operator">*</span> nr_pages_scanned<span class="token punctuation">)</span> <span class="token operator">/</span> shrinker<span class="token operator">-></span>seeks<span class="token punctuation">;</span>
delta <span class="token operator">*</span><span class="token operator">=</span> max_pass<span class="token punctuation">;</span>
<span class="token function">do_div</span><span class="token punctuation">(</span>delta<span class="token punctuation">,</span> lru_pages <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
total_scan <span class="token operator">+</span><span class="token operator">=</span> delta<span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>total_scan <span class="token operator"><</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token function">printk</span><span class="token punctuation">(</span>KERN_ERR <span class="token string">"shrink_slab: %pF negative objects to "</span>
<span class="token string">"delete nr=%ld\n"</span><span class="token punctuation">,</span>
shrinker<span class="token operator">-></span>shrink<span class="token punctuation">,</span> total_scan<span class="token punctuation">)</span><span class="token punctuation">;</span>
total_scan <span class="token operator">=</span> max_pass<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">//如果delta偏小,意味着系统中inactive的缓存偏少,我们回收的额度也不能设置太大</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>delta <span class="token operator"><</span> max_pass <span class="token operator">/</span> <span class="token number">4</span><span class="token punctuation">)</span>
total_scan <span class="token operator">=</span> <span class="token function">min</span><span class="token punctuation">(</span>total_scan<span class="token punctuation">,</span> max_pass <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//控制回收总额上限,避免死循环</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>total_scan <span class="token operator">></span> max_pass <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">)</span>
total_scan <span class="token operator">=</span> max_pass <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">;</span>
<span class="token function">trace_mm_shrink_slab_start</span><span class="token punctuation">(</span>shrinker<span class="token punctuation">,</span> shrink<span class="token punctuation">,</span> nr<span class="token punctuation">,</span>
nr_pages_scanned<span class="token punctuation">,</span> lru_pages<span class="token punctuation">,</span>
max_pass<span class="token punctuation">,</span> delta<span class="token punctuation">,</span> total_scan<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//循环回收缓存</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span>total_scan <span class="token operator">>=</span> batch_size<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">int</span> nr_before<span class="token punctuation">;</span>
<span class="token comment">//记录处理前缓存数量</span>
nr_before <span class="token operator">=</span> <span class="token function">do_shrinker_shrink</span><span class="token punctuation">(</span>shrinker<span class="token punctuation">,</span> shrink<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//回收后缓存数量</span>
shrink_ret <span class="token operator">=</span> <span class="token function">do_shrinker_shrink</span><span class="token punctuation">(</span>shrinker<span class="token punctuation">,</span> shrink<span class="token punctuation">,</span>
batch_size<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>shrink_ret <span class="token operator">==</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token comment">//统计此次回收的缓存数量</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>shrink_ret <span class="token operator"><</span> nr_before<span class="token punctuation">)</span>
ret <span class="token operator">+</span><span class="token operator">=</span> nr_before <span class="token operator">-</span> shrink_ret<span class="token punctuation">;</span>
<span class="token function">count_vm_events</span><span class="token punctuation">(</span>SLABS_SCANNED<span class="token punctuation">,</span> batch_size<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//减少扫描总额</span>
total_scan <span class="token operator">-</span><span class="token operator">=</span> batch_size<span class="token punctuation">;</span>
<span class="token function">cond_resched</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">//如果剩下的额度不够一个batch_size,留着下次使用,记录在nr_in_batch</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>total_scan <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span>
new_nr <span class="token operator">=</span> <span class="token function">atomic_long_add_return</span><span class="token punctuation">(</span>total_scan<span class="token punctuation">,</span>
<span class="token operator">&</span>shrinker<span class="token operator">-></span>nr_in_batch<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">else</span>
new_nr <span class="token operator">=</span> <span class="token function">atomic_long_read</span><span class="token punctuation">(</span><span class="token operator">&</span>shrinker<span class="token operator">-></span>nr_in_batch<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">trace_mm_shrink_slab_end</span><span class="token punctuation">(</span>shrinker<span class="token punctuation">,</span> shrink_ret<span class="token punctuation">,</span> nr<span class="token punctuation">,</span> new_nr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token function">up_read</span><span class="token punctuation">(</span><span class="token operator">&</span>shrinker_rwsem<span class="token punctuation">)</span><span class="token punctuation">;</span>
out:
cond_resched();
return ret;
}
空闲slab缓存计算和回收都是在do_shrinker_shrink完成,它其实调用的是一个函数指针,不同slab管理区有自己定义的shrink函数,第三个入参nr_to_scan为0时,是计算空闲slab缓存;不为空时,表示扫描和回收缓存的数量。
static inline int do_shrinker_shrink(struct shrinker *shrinker,
struct shrink_control *sc,
unsigned long nr_to_scan)
{
int objects;
sc->nr_to_scan = nr_to_scan;
objects = (*shrinker->shrink)(shrinker, sc);
<span class="token keyword">if</span> <span class="token punctuation">(</span>objects <span class="token operator"><</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token keyword">return</span> INT_MAX<span class="token punctuation">;</span>
<span class="token keyword">return</span> objects<span class="token punctuation">;</span>
}
总的来说,drop_slab就是调用每个slab管理区定义的shrink函数,先计算出可回收的slab缓存数量,然后确定扫描数量,最后调用shrink函数执行缓存扫描和回收。
[转帖]vm内核参数之缓存回收drop_caches的更多相关文章
- vm内核参数优化设置
http://www.cnblogs.com/wjoyxt/archive/2014/06/08/3777042.html (1)vm.overcommit_memory 执行grep -i com ...
- linux 内核参数VM调优 之 参数调节和场景分析
1. pdflush刷新脏数据条件 (linux IO 内核参数调优 之 原理和参数介绍)上一章节讲述了IO内核调优介个重要参数参数. 总结可知cached中的脏数据满足如下几个条件中一个或者多个的时 ...
- 谨慎调整内核参数:vm.min_free_kbytes
内核参数:内存相关 内存管理从三个层次管理内存,分别是node, zone ,page; 64位的x86物理机内存从高地址到低地址分为: Normal DMA32 DMA.随着地址降低. [root@ ...
- Centos内核参数
内核参数 abi.vsyscall32 = 1 在2.6.25版本以后的x86-64内核中,默认启用了VDSO32. 虚拟动态共享对象 http://man7.org/linux/man-pages ...
- oracle内核参数详解
一.前言 在生产中,我们安装oracle数据库时,为达到最优我们需要对操作系统的内核参数进行一定的调整.主要从内存.cpu.io以及网络等方面,根据实际情况进行调整.以下参数可供大家参考,如有不当之处 ...
- LINUX内核参数网络相关
有助于提高网络性能和吞吐量的参数 net.core.somaxconn = 128 已完成连接队列(completed connection queue) (1)三次握手已经完成,但还未被应用层接收( ...
- linux下TCP/IP及内核参数优化调优(转)
Linux下TCP/IP及内核参数优化有多种方式,参数配置得当可以大大提高系统的性能,也可以根据特定场景进行专门的优化,如TIME_WAIT过高,DDOS攻击等等. 如下配置是写在sysctl.con ...
- Linux内核参数配置
Linux在系统运行时修改内核参数(/proc/sys与/etc/sysctl.conf),而不需要重新引导系统,这个功能是通过/proc虚拟文件系统实现的. 在/proc/sys目录下存放着大多数的 ...
- /proc/sys/vm/ 内存参数
linux下proc里关于磁盘性能的参数 http://blog.csdn.net/eroswang/article/details/6126646 我们在磁盘写操作持续繁忙的服务器上曾经碰到一个特 ...
- Nginx的10万并发内核参数优化
关于内核参数的优化: net.ipv4.tcp_max_tw_buckets = 6000timewait的数量,默认是180000.net.ipv4.ip_local_port_range = 10 ...
随机推荐
- C语言编程需要掌握的核心要点有哪些? 编程大神为你总结了这20个
摘要:C语言作为编程的入门语言,学习者如何快速掌握其核心知识点,面对茫茫书海,似乎有点迷茫.为了让各位快速地掌握C语言的知识内容,在这里对相关的知识点进行了归纳. 引言 笔者有十余年的C++开发经验, ...
- Codeforces Round 913 (Div. 3)
CF1907总结 A. Rook 题面翻译 给出车在国际象棋棋盘中的位置,输出其可到达的坐标(不必在意顺序). 车可以横着或竖着走任意格数. 分析 题意明了,输出车所在行和列所有格子的序号(除车所在位 ...
- zsh踩坑记录
1. zsh: no matches found: uvicorn[standard] 方法一 # 在~/.zshrc中添加下面这句话 setopt no_nomatch # 然后source ~/. ...
- sqlalchemy 查询已存在表(无model定义)
已存在的表信息如下,该表不是由sqlalchemy创建.想要对该表做CRUD,可以通过sqlalchemy提供的一个解析方法完成. 示例代码: from sqlalchemy import creat ...
- vue3.0 学习使用记录
vue学习 在最近的一次项目中,前端js框架里使用了vue.vue的编程思想比较新颖,用起来也感觉高效便捷.由于个人原因,做完这个项目之后可能就接触不到前端的内容了,所以记录这个项目中对vue的学习和 ...
- 服务器上TIME_WAIT过多怎么处理
正常情况下,TIME_WAIT是需要存在的 为了保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文,服务 ...
- 0x53 动态规划-区间DP
A: 石子合并 所求问题:1到n这些石子合并最少需要多少代价 由于石子合并的顺序可以任意,我们将石子分为两个部分 子问题:1到k这堆石子合并,k+1到n这堆石子合并,再把两堆石子合并,需要多少代价\( ...
- iOS Class Guard github用法、工作原理和安装详解及使用经验总结
iOS Class Guard github用法.工作原理和安装详解及使用经验总结 iOS Class Guard是一个用于OC类.协议.属性和方法名混淆的命令行工具.它是class-dump的扩展 ...
- 畅捷通的 Serverless 探索实践之路
作者:计缘 畅捷通介绍 畅捷通是中国领先的小微企业财税及业务云服务提供商,成立于2010年.畅捷通在2021年中国小微企业云财税市场份额排名第一,在产品前瞻性及行业全覆盖方面领跑市场,位居中国小微企业 ...
- Serverless 的前世今生
作者:刘宇(江昱) 从云计算到Serverless架构 大家好,我是阿里云 Serverless 产品经理刘宇,很高兴可以和大家一起探索 Serverless 架构的前世今生. 从云计算到云原生再到 ...