Linux 3.2中回写机制的变革

原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 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_infobdi_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. }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中的脏数据回是指将缓存中的脏数据(即已经被修改但还未入磁盘的数据)回到存储介质中的过程。在Linux中,有两种方式触发脏数据回:阈值触发和定时触发。 阈值触发是指当脏数据的量达到一定的阈值时,就会触发回操作。具体来说,Linux中有两个参数来控制脏数据的阈值:dirty_background_ratio和dirty_ratio。dirty_background_ratio表示当脏数据占总内存的一定比例时,会触发后台回进程,将部分脏数据回到磁盘;dirty_ratio则表示当脏数据占总内存的另一定比例时,会触发前台回进程,将更多的脏数据回到磁盘。这两个参数的默认值分别为10%和20%。 定时触发是指系统会定期检查脏数据的数量,并在一定的时间间隔后触发回操作。具体来说,Linux中有一个回刷任务(flusher thread),它会定期检查脏数据的数量,如果超过一定的阈值,就会触发回操作。 需要注意的是,当脏数据达到阈值触发回时,并不会等待回完成,而是直接唤醒回进程开始回操作。最终的回收工作仍然由writeback进程来完成。 综上所述,Linux中的脏数据回可以通过阈值触发和定时触发来进行。阈值触发是当脏数据的量达到一定比例时触发回,而定时触发是定期检查脏数据的数量并触发回操作。这样可以保证系统及时将脏数据回到磁盘,确保数据的一致性和持久性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Linux 脏数据回刷参数与调优](https://blog.csdn.net/m0_51717456/article/details/124582256)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值