pdflush机制
在做进程安全监控的时候,拍脑袋决定的,如果发现一个进程在D状态时,即TASK_UNINTERRUPTIBLE(不可中断的睡眠状态),时间超过了8min,就将系统panic掉。恰好DB组做日志时,将整个log缓存到内存中,最后刷磁盘,结果系统就D状态了很长时间,自然panic了,中间涉及到Linux的缓存写回刷磁盘的一些机制和调优方法,写一下总结。
目前机制需要将脏页刷回到磁盘一般是以下情况:
- 脏页缓存占用的内存太多,内存空间不足;
- 脏页已经更改了很长时间,时间上已经到了临界值,需要及时刷新保持内存和磁盘上数据一致性;
- 外界命令强制刷新脏页到磁盘
- write写磁盘时检查状态刷新
内核使用pdflush线程刷新脏页到磁盘,pdflush线程个数在2和8之间,可以通过/proc/sys/vm/nr_pdflush_threads文件直接查看,具体策略机制参看源码函数__pdflush。
一、内核其他模块强制刷新
先说一下第一种和第三种情况:当内存空间不足或外界强制刷新的时候,脏页的刷新是通过调用wakeup_pdflush函数实现的,调用其函数的有do_sync、free_more_memory、try_to_free_pages。wakeup_pdflush的功能是通过background_writeout的函数实现的:
static void background_writeout(unsigned long _min_pages)
{
long min_pages = _min_pages;
struct writeback_control wbc = {
.bdi = NULL,
.sync_mode = WB_SYNC_NONE,
.older_than_this = NULL,
.nr_to_write = ,
.nonblocking = ,
}; for ( ; ; ) {
struct writeback_state wbs;
long background_thresh;
long dirty_thresh; get_dirty_limits(&wbs, &background_thresh, &dirty_thresh, NULL);
if (wbs.nr_dirty + wbs.nr_unstable < background_thresh
&& min_pages <= )
break;
wbc.encountered_congestion = ;
wbc.nr_to_write = MAX_WRITEBACK_PAGES;
wbc.pages_skipped = ;
writeback_inodes(&wbc);
min_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
if (wbc.nr_to_write > || wbc.pages_skipped > ) {
/* Wrote less than expected */
blk_congestion_wait(WRITE, HZ/);
if (!wbc.encountered_congestion)
break;
}
}
}
background_writeout进到一个死循环里面,通过get_dirty_limits获取脏页开始刷新的临界值background_thresh,即为dirty_background_ratio的总内存页数百分比,可以通过proc接口/proc/sys/vm/dirty_background_ratio调整,一般默认为10。当脏页超过临界值时,调用writeback_inodes写MAX_WRITEBACK_PAGES(1024)个页,直到脏页比例低于临界值。
二、内核定时器启动刷新
内核在启动的时候在page_writeback_init初始化wb_timer定时器,超时时间是dirty_writeback_centisecs,单位是0.01秒,可以通过/proc/sys/vm/dirty_writeback_centisecs调节。wb_timer的触发函数是wb_timer_fn,最终是通过wb_kupdate实现。
static void wb_kupdate(unsigned long arg)
{
sync_supers();
get_writeback_state(&wbs);
oldest_jif = jiffies - (dirty_expire_centisecs * HZ) / ;
start_jif = jiffies;
next_jif = start_jif + (dirty_writeback_centisecs * HZ) / ;
nr_to_write = wbs.nr_dirty + wbs.nr_unstable +
(inodes_stat.nr_inodes - inodes_stat.nr_unused);
while (nr_to_write > ) {
wbc.encountered_congestion = ;
wbc.nr_to_write = MAX_WRITEBACK_PAGES;
writeback_inodes(&wbc);
if (wbc.nr_to_write > ) {
if (wbc.encountered_congestion)
blk_congestion_wait(WRITE, HZ/);
else
break; /* All the old data is written */
}
nr_to_write -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
}
if (time_before(next_jif, jiffies + HZ))
next_jif = jiffies + HZ;
if (dirty_writeback_centisecs)
mod_timer(&wb_timer, next_jif);
}
上面的代码没有拷贝全。内核首先将超级块信息刷新到文件系统上,然后获取oldest_jif作为wbc的参数只刷新已修改时间大于dirty_expire_centisecs的脏页,dirty_expire_centisecs参数可以通过/proc/sys/vm/dirty_expire_centisecs调整。
三、WRITE写文件刷新缓存
用户态使用WRITE函数写文件时也有可能要刷新脏页,generic_file_buffered_write函数会在将写的内存页标记为脏之后,根据条件刷新磁盘以平衡当前脏页比率,参看balance_dirty_pages_ratelimited函数:
void balance_dirty_pages_ratelimited(struct address_space *mapping)
{
static DEFINE_PER_CPU(int, ratelimits) = ;
long ratelimit; ratelimit = ratelimit_pages;
if (dirty_exceeded)
ratelimit = ; /*
* Check the rate limiting. Also, we do not want to throttle real-time
* tasks in balance_dirty_pages(). Period.
*/
if (get_cpu_var(ratelimits)++ >= ratelimit) {
__get_cpu_var(ratelimits) = ;
put_cpu_var(ratelimits);
balance_dirty_pages(mapping);
return;
}
put_cpu_var(ratelimits);
}
balance_dirty_pages_ratelimited函数通过ratelimit_pages调节刷新(调用balance_dirty_pages函数)的次数,每ratelimit_pages次调用才会刷新一次,具体刷新过程看balance_dirty_pages函数:
static void balance_dirty_pages(struct address_space *mapping)
{
struct writeback_state wbs;
long nr_reclaimable;
long background_thresh;
long dirty_thresh;
unsigned long pages_written = ;
unsigned long write_chunk = sync_writeback_pages(); struct backing_dev_info *bdi = mapping->backing_dev_info; for (;;) {
struct writeback_control wbc = {
.bdi = bdi,
.sync_mode = WB_SYNC_NONE,
.older_than_this = NULL,
.nr_to_write = write_chunk,
}; get_dirty_limits(&wbs, &background_thresh,
&dirty_thresh, mapping);
nr_reclaimable = wbs.nr_dirty + wbs.nr_unstable;
if (nr_reclaimable + wbs.nr_writeback <= dirty_thresh)
break; if (!dirty_exceeded)
dirty_exceeded = ; /* Note: nr_reclaimable denotes nr_dirty + nr_unstable.
* Unstable writes are a feature of certain networked
* filesystems (i.e. NFS) in which data may have been
* written to the server's write cache, but has not yet
* been flushed to permanent storage.
*/
if (nr_reclaimable) {
writeback_inodes(&wbc);
get_dirty_limits(&wbs, &background_thresh,
&dirty_thresh, mapping);
nr_reclaimable = wbs.nr_dirty + wbs.nr_unstable;
if (nr_reclaimable + wbs.nr_writeback <= dirty_thresh)
break;
pages_written += write_chunk - wbc.nr_to_write;
if (pages_written >= write_chunk)
break; /* We've done our duty */
}
blk_congestion_wait(WRITE, HZ/);
} if (nr_reclaimable + wbs.nr_writeback <= dirty_thresh && dirty_exceeded)
dirty_exceeded = ; if (writeback_in_progress(bdi))
return; /* pdflush is already working this queue */ /*
* In laptop mode, we wait until hitting the higher threshold before
* starting background writeout, and then write out all the way down
* to the lower threshold. So slow writers cause minimal disk activity.
*
* In normal mode, we start background writeout at the lower
* background_thresh, to keep the amount of dirty memory low.
*/
if ((laptop_mode && pages_written) ||
(!laptop_mode && (nr_reclaimable > background_thresh)))
pdflush_operation(background_writeout, );
}
函数走进一个死循环,通过get_dirty_limits获取dirty_background_ratio和dirty_ratio对应的内存页数值,当24行做判断,如果脏页大于dirty_thresh,则调用writeback_inodes开始刷缓存到磁盘,如果一次没有将脏页比率刷到dirty_ratio之下,则用blk_congestion_wait阻塞写,然后反复循环,直到比率降低到dirty_ratio;当比率低于dirty_ratio之后,但脏页比率大于dirty_background_ratio,则用pdflush_operation启用background_writeout,pdflush_operation是非阻塞函数,唤醒pdflush后直接返回,background_writeout在有pdflush调用。
如此可知:WRITE写的时候,缓存超过dirty_ratio,则会阻塞写操作,回刷脏页,直到缓存低于dirty_ratio;如果缓存高于background_writeout,则会在写操作时,唤醒pdflush进程刷脏页,不阻塞写操作。
四,问题总结
导致进程D状态大部分是因为第3种和第4种情况:有大量写操作,缓存由Linux系统管理,一旦脏页累计到一定程度,无论是继续写还是fsync刷新,都会使进程D住。
pdflush机制的更多相关文章
- Linux 3.2中回写机制的变革
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://alanwu.blog.51cto.com/3652632/1109952 wri ...
- linux下数据同步、回写机制分析
一.前言在linux2.6.32之前,linux下数据同步是基于pdflush线程机制来实现的,在linux2.6.32以上的版本,内核彻底删掉了pdflush机制,改为了基于per-bdi线程来实现 ...
- linux io优化
场景:xml文件解析入库:并备份 问题:磁盘io异常,经常100%busy: linux io优化方法: 1.修改磁盘挂着参数,修改为writeback模式:对于文件读取频繁的可以设置noatime: ...
- 【转】磁盘I/O那些事
背景 计算机硬件性能在过去十年间的发展普遍遵循摩尔定律,通用计算机的CPU主频早已超过3GHz,内存也进入了普及DDR4的时代.然而传统硬盘虽然在存储容量上增长迅速,但是在读写性能上并无明显提升,同时 ...
- Linux文件读写机制及优化方式
导读 Linux是一个可控性强的,安全高效的操作系统.本文只讨论Linux下文件的读写机制,不涉及不同读取方式如read,fread,cin等的对比,这些读取方式本质上都是调用系统api read,只 ...
- 【转】Linux Writeback机制分析
1. bdi是什么? bdi,即是backing device info的缩写,顾名思义它描述备用存储设备相关描述信息,这在内核代码里用一个结构体backing_dev_info来表示. bdi,备用 ...
- kswapd0、kjournald、pdflush、kblocked、migration进程含义 转
kswapd0.kjournald.pdflush.kblocked.migration进程含义 1.kswapd0 Linux uses kswapd for virtual memory mana ...
- Linux页快速缓存与回写机制分析
參考 <Linux内核设计与实现> ******************************************* 页快速缓存是linux内核实现的一种主要磁盘缓存,它主要用来降低 ...
- pdflush的工作原理
大家知道,在linux操作系统中,写操作是异步的,即写操作返回的时候数据并没有真正写到磁盘上,而是先写到了系统cache里,随后由pdflush内核线程将系统中的脏页写到磁盘上,在下面几种情况下,系统 ...
随机推荐
- 【代码笔记】iOS-MBProgressHUDDemo
一,工程图. 二,代码. RootViewController.h #import <UIKit/UIKit.h> //加入头文件 #import "MBProgressHUD. ...
- react 传递非state给子元素的注意事项
我们是使用react的时候,其实很多情况都不需要使用state去存储值,如果不涉及页面渲染的值,我们往往可以使用 this.xxx的方式:这样可以提高组件的性能,避免不必要的 re_render 带来 ...
- 设定linux为多用户模式
1.显示系统已经安装的组件,和可以安装的组件: #yum grouplist 2.如果系统安装之初采用最小化安装,没有安装xwindow,那么先安装: #yum groupinstall " ...
- 解决跨域问题之anywhere
anywhere搭建服务,ionic PC端和手机端可以通过网址来查看网页效果.解决跨域问题 大家都知道编写完HTML代码后,可以直接在pc端的浏览器查看,但现在手机端越来越广泛了,想跟在pc端查看网 ...
- 【three.js练习程序】创建简单物理场景
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- chrome中workspace配置达到同步修改本地文件的作用
在前端开发中,我们经常需要在浏览器中进行调试,特别是一些样式的修改,如果你还是先在浏览器elements中调试好在复制到本地文件,那就真的out了. chrome浏览器的workspace功能完全可以 ...
- python-异常处理try_except
异常处理try-except 在我们写程序的时候经常会遇到一些异常或错误,导致程序终止 当我们使用计算器时,用10除以0会提示 一个简单的错误代码(10/0) a = 10 / 0 print(&qu ...
- Python实例---爬去酷狗音乐
项目一:获取酷狗TOP 100 http://www.kugou.com/yy/rank/home/1-8888.html 排名 文件&&歌手 时长 效果: 附源码: import t ...
- 谁对EXTJS熟悉,有关关闭panel的问题?
谁对EXTJS熟悉,有关关闭panel的问题?比如:我在A.js 中写了一个 var win = new Ext.Window( { b.js }); win.show(); 打开了一 ...
- java字节码的工具(含IDEA插件)
转:https://blog.csdn.net/qq_34039315/article/details/78561493 javap方式 最基础的方式,此处不做介绍 windows下的工具jclass ...