原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://alanwu.blog.51cto.com/3652632/1109952
writeback机制模型
 
在Linux-3.2新内核中,page cache和buffer cache的刷新机制发生了改变。放弃了原有的pdflush机制,改成了bdi_writeback机制。这种变化主要解决原有pdflush机制存在的一个问题:在多磁盘的系统中,pdflush管理了所有磁盘的page/buffer cache,从而导致一定程度的IO性能瓶颈。bdi_writeback机制为每个磁盘都创建一个线程,专门负责这个磁盘的page cache或者buffer cache的数据刷新工作,从而实现了每个磁盘的数据刷新程序在线程级的分离,这种处理可以提高IO性能。
 
writeback机制的基本原理可以描述如下:
 

在Linux内核中有一个常驻内存的线程bdi_forker_thread,该线程负责为bdi_object创建writeback线程,同时检测如果writeback线程长时间处于空闲状态,bdi_forker_thread线程便会将其进行销毁。bdi_forker_thread在系统中只有一个,其会被定时唤醒,检查全局链表bdi_list队列中是否存在dirty的数据需要刷新到磁盘。如果存在dirty数据并且对应bdi的writeback线程还没有被创建,bdi_forker_thread会为该bdi创建一个writeback的线程进行写回操作。
 
writeback线程被创建之后会处理等待的work。writeback线程拥有一个定时器会周期性唤醒这个线程处理相应的work。当用户(page cache/buffer cache)有需要处理的inode时,将inode挂载到writeback-> b_dirty链表中,然后唤醒writeback线程去处理相应的dirty_page。inode链表就是writeback线程需要处理的数据;work链表就是控制处理过程中的一些策略,不同的策略可以定义成不同的任务。
 
通过上述模型,对于块设备或者文件系统而言,实现dirty page的后台刷新主要做如下几个方面的工作:
 
1,将自己的bdi注册到系统的bdi链表中,通过bdi_forker_thread实现对bdi对象的管理,从而可以实现writeback线程的动态创建、销毁。每个块设备和文件系统都有自己的bdi对象。Ext3文件系统在创建的时候会生成superblock对象,系统会将底层块设备的backing_device关系到这个superblock对象上(在set_bdev_super函数中完成)。如果是块设备的话,在add_disk的时候直接从request_queue中得到bdi对象,然后对其进行初始化。注册bdi对象使用bdi_register_dev函数,对于ext3之类的文件系统不需要重新注册bdi对象,因为其本身就采用了底层块设备的bdi对象。
 
2,将需要刷新的inode节点挂载到bdi对象所属的writeback->b_dirty上,如果有特殊的work需要writeback线程完成,那么提交一个work即可;如果是通常的周期性刷新,writeback线程会自动创建相应的work。
 
3,操作writeback的唤醒定时器延迟唤醒writeback线程,或者直接唤醒线程,从而使得inode中radix tree上的dirty page刷新到磁盘。
 
bdi对象的注册
 
每个块设备在创建的时候会注册bdi对象(参见add_disk函数),这是Linux-3.2内核不同的地方。文件系统在mount的时候会创建superblock对象,并且通过底层块设备的request queue获取bdi对象(mount_bdev->sget->set_bdev_super)。所以,像ext3之类的文件系统都不需要重新注册bdi对象。当然,如果文件系统重新创建了一个bdi对象,那么还需要调用bdi_register_dev函数注册bdi对象。
 
小结
 
本文对linux-3.2中的writeback机制模型进行了阐述,后面还会对writeback机制中的关键函数进行分析说明。该机制是对老系统(Linux-2.6.23等)中pdflush机制的替代,其最重要的变化是每个块设备都分配了writeback线程,使得回写的IO流在各个磁盘之间独立,从而从机制上提高了IO的吞吐量。

本文出自 “存储之道” 博客,请务必保留此出处http://alanwu.blog.51cto.com/3652632/1109952

初始化默认的后备存储器default_backing_dev_info

  1. static int __init default_bdi_init(void)
  2. {
  3. int err;
  4. /*创建同步每个后备存储器的超级块的线程*/
  5. sync_supers_tsk = kthread_run(bdi_sync_supers, NULL, "sync_supers");
  6. BUG_ON(IS_ERR(sync_supers_tsk));
  7. /*初始化一个定时器,该定时器控制同步超级块的周期,每隔dirty_writeback_interval去唤醒一次sync_supers_tsk,从而同步超级块。
  8. dirty_writeback_interval可以通过修改/proc/sys/vm/下的dirty_writeback_centisecs来修改,默认值是500,单位是10ms
  9. 定时器函数sync_supers_timer_fn用于唤醒同步超级块的线程sync_supers_tsk,并且更新定时器的到期时间,具体实现如下*/
  10. setup_timer(&sync_supers_timer, sync_supers_timer_fn, 0);
  11. /*用于更新定时器的到期时间,详见下面代码。*/
  12. bdi_arm_supers_timer();
  13. /*初始化default_backing_dev_info的成员变量,初始化相关的链表,相关的变量赋初值等操作,请读者自行阅读。*/
  14. err = bdi_init(&default_backing_dev_info);
  15. if (!err)
  16. /*调用bdi_register注册默认的后备存储器default_backing_dev_info到bdi_list链表,并创建默认的backing_dev_info管理线程,
  17. 用于管理其他的后备存储器的数据同步线程的创建和销毁,所有的后备存储器在初始化时都会调用bdi_register注册到bdi_list链表中。
  18. bdi_register详见下文分析。*/
  19. bdi_register(&default_backing_dev_info, NULL, "default");
  20. /*初始化空的后备存储器,可以忽略。*/
  21. err = bdi_init(&noop_backing_dev_info);
  22. return err;
  23. }

sync_supers_timer_fn函数唤醒超级块数据同步线程,然后重设定时器。

  1. static void sync_supers_timer_fn(unsigned long unused)
  2. {
  3. wake_up_process(sync_supers_tsk);
  4. bdi_arm_supers_timer();
  5. }

bdi_arm_supers_timer函数重设定时器

  1. void bdi_arm_supers_timer(void)
  2. {
  3. unsigned long next;
  4. if (!dirty_writeback_interval)
  5. return;
  6. next = msecs_to_jiffies(dirty_writeback_interval * 10) + jiffies;
  7. mod_timer(&sync_supers_timer, round_jiffies_up(next));
  8. }

三、bdi_register()函数分析

bdi_register函数用于注册后备存储器到全局链表bdi_list上,并且判断如果是默认的后备存储器default_backing_dev_info则创建bdi-default线程,用于管理创建或销毁所有后备存储器相关的同步回写线程。

  1. int bdi_register(struct backing_dev_info *bdi, struct device *parent,
  2. const char *fmt, ...)
  3. {
  4. va_list args;
  5. struct device *dev;
  6. if (bdi->dev)    /*
    The driver needs to use separate queues per device */
  7. return 0;
  8. va_start(args, fmt);
  9. dev = device_create_vargs(bdi_class, parent, MKDEV(0, 0), bdi, fmt, args);
  10. va_end(args);
  11. if (IS_ERR(dev))
  12. return PTR_ERR(dev);
  13. bdi->dev = dev;
  14. /*
  15. * Just start the forker thread for our default backing_dev_info,
  16. * and add other bdi's to the list. They will get a thread created
  17. * on-demand when they need it.
  18. */
  19. if (bdi_cap_flush_forker(bdi)) {
  20. struct bdi_writeback *wb = &bdi->wb;
  21. wb->task = kthread_run(bdi_forker_thread, wb, "bdi-%s",
  22. dev_name(dev));
  23. if (IS_ERR(wb->task))
  24. return PTR_ERR(wb->task);
  25. }
  26. bdi_debug_register(bdi, dev_name(dev));
  27. set_bit(BDI_registered, &bdi->state);
  28. spin_lock_bh(&bdi_lock);
  29. list_add_tail_rcu(&bdi->bdi_list, &bdi_list);
  30. spin_unlock_bh(&bdi_lock);
  31. trace_writeback_bdi_register(bdi);
  32. return 0;
  33. }

Linux 3.2中回写机制的变革的更多相关文章

  1. Linux页快速缓存与回写机制分析

    參考 <Linux内核设计与实现> ******************************************* 页快速缓存是linux内核实现的一种主要磁盘缓存,它主要用来降低 ...

  2. linux块设备的IO调度算法和回写机制

    ************************************************************************************** 參考: <Linux ...

  3. linux下数据同步、回写机制分析

    一.前言在linux2.6.32之前,linux下数据同步是基于pdflush线程机制来实现的,在linux2.6.32以上的版本,内核彻底删掉了pdflush机制,改为了基于per-bdi线程来实现 ...

  4. 【转】linux设备驱动程序中的阻塞机制

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...

  5. rabbitMQ的简单实例——amqp协议带数据回写机制

    rabbitMQ是一种高性能的消息队列,支持或者说它实现了AMQP协议(advanced message queue protocol高级消息队列协议). 下面简单讲一讲一个小例子.我们首先要部署好r ...

  6. linux x86内核中的分页机制

    Linux采用了通用的四级分页机制,所谓通用就是指Linux使用这种分页机制管理所有架构的分页模型,即便某些架构并不支持四级分页.对于常见的x86架构,如果系统是32位,二级分页模型就可满足系统需求: ...

  7. Linux内核设计与实现 总结笔记(第十六章)页高速缓存和页回写

    页高速缓存是Linux内核实现磁盘缓存.磁盘告诉缓存重要源自:第一,访问磁盘的速度要远远低于访问内存. 第二,数据一旦被访问,就很有可能在短期内再次被访问到.这种短时期内集中访问同一片数据的原理称作临 ...

  8. 多线程中的信号机制--signwait()函数【转】

    本文转载自:http://blog.csdn.net/yusiguyuan/article/details/14237277 在Linux的多线程中使用信号机制,与在进程中使用信号机制有着根本的区别, ...

  9. 《Linux内核设计与实现》读书笔记(十六)- 页高速缓存和页回写

    好久没有更新了... 主要内容: 缓存简介 页高速缓存 页回写 1. 缓存简介 在编程中,缓存是很常见也很有效的一种提高程序性能的机制. linux内核也不例外,为了提高I/O性能,也引入了缓存机制, ...

随机推荐

  1. Action的创建和配置

    计应134(实验班) 李佳鸿 Action类—Struts组件 在RequestProcessor类预处理请求时,在创建了Action的实例后,就调用自身的processActionPerform() ...

  2. SQL语句流程函数

    本人因为今天用到了流程函数,顿时感觉语法生疏啊,为了防止以后忘记,故写此篇!!! 流程函数是MySQL相对常用的一类函数, 用户可以使用这类函数在一个SQL语句中实现条件选择, 这样能够提高效率. 下 ...

  3. Linux云自动化运维第四课

    Linux云自动化运维第四课 一.vim 1.vim光标移动 1)在命令模式下 :数字  ###移动到指定的行 G  ###文件最后一行 gg  ###文件第一行 2)在插入模式下 i  ###光标所 ...

  4. Android-自定义控件之时针-霞辉

    注释已经比较详细了,废话就不多说了.贴代码了 时针分针秒钟都做上去了,采用的方法也很简单,仔细看一会就能看懂 自定义View类 package com.xh.mytime; import java.u ...

  5. 用Caffe生成对抗样本

    同步自我的知乎专栏:https://zhuanlan.zhihu.com/p/26122612 上篇文章 瞎谈CNN:通过优化求解输入图像 - 知乎专栏 中提到过对抗样本,这篇算是针对对抗样本的一个小 ...

  6. npm命令点滴记录

    关于-save-dve后缀命令: 当你为你的模块安装一个依赖模块时,正常情况下你得先安装他们(在模块根目录下npm install module-name),然后连同版本号手动将他们添加到模块配置文件 ...

  7. 性能测试培训:Ajax接口级性能测试之jmeter版

    性能测试培训:Ajax接口级性能测试之jmeter版   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest认为工具 ...

  8. 老李分享:为何要使用 Web Services

    老李分享:为何要使用 Web Services   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询q ...

  9. AngularJS1.X学习笔记2-数据绑定

    上一篇从整体上认识了Angular,从现在开始更加深入的学习Angular的特性.本次学习的是数据绑定.应该所有的MVC框架都会用到数据绑定,比如我所知道的ThinkPHP.struts等,只有实现了 ...

  10. 深度学习实践系列(3)- 使用Keras搭建notMNIST的神经网络

    前期回顾: 深度学习实践系列(1)- 从零搭建notMNIST逻辑回归模型 深度学习实践系列(2)- 搭建notMNIST的深度神经网络 在第二篇系列中,我们使用了TensorFlow搭建了第一个深度 ...