博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一个IO的传奇一生 (3) ---- 数据回写
阅读量:5985 次
发布时间:2019-06-20

本文共 2867 字,大约阅读时间需要 9 分钟。

数据回写

对于EXT3文件系统而言,在绝大多数情况下,IO请求走到page cache之后就认为这个IO处理已经完成了。用户的IO请求被放入Cache之后,用户操作结束。实际上,此时的IO处境非常的危险,如果系统在此时Crash,那么内存中缓存的数据将会彻底丢失。为了保证数据不丢失,需要有一种机制及时的将缓存中的数据同步(回写)到磁盘。对于2.6.23版本的Linux,采用了Pdflush的数据同步机制;在3.2版本的Linux中,采用了writeback的数据同步机制。这种机制的变革主要考虑了系统性能提升方面的因素。关于这两种机制的对比可以参考文章《》。

Linux3.2中,当一个IO数据被写入Page缓存之后,EXT3文件系统会调用ext3_ordered_write_end函数(注意,在Linux2.6.23中调用aops->commit_write,会有所差别)结束整个过程。在该函数中主要完成pagedirty标志位的设置、日志信息的清除工作。其中,设置page页为dirty的操作就是用来唤醒(或者创建)一个writeback线程去处理缓存中的脏数据。设置pagedirty标志的核心函数是__set_page_dirty(调用关系为:ext3_ordered_write_endàblock_write_endà__block_commit_writeàmark_buffer_dirtyà__set_page_dirty)。__set_page_dirty函数说明如下:

static void __set_page_dirty(struct page *page,struct address_space *mapping, int warn){spin_lock_irq(&mapping->tree_lock);if (page->mapping) { /* Race with truncate? */WARN_ON_ONCE(warn && !PageUptodate(page));account_page_dirtied(page, mapping);/* 设置radix tree中的Tag标志 */radix_tree_tag_set(&mapping->page_tree,page_index(page), PAGECACHE_TAG_DIRTY);}spin_unlock_irq(&mapping->tree_lock);/* 将inode加入到writeback线程处理的事务队列中 */__mark_inode_dirty(mapping->host, I_DIRTY_PAGES);}

__set_page_dirty函数中主要完成了两件事情:

1)radix tree中设置需要回写的page页为dirty。我们知道Linux为了加速dirty页的检索,在radix tree中采用了tag机制。Tag本质上就是一种flagradix tree的每层都有tag,上层的tag信息是下层信息的汇总。所以,如果radix tree中没有脏页,那么最顶层的tag就不会被标识成dirty。如果radix tree中有脏页,脏页所在的那条分支tag才会出现dirty flag。显然,采用这种方法可以加速脏页的检索。如下图所示,如果0x00000010地址所在的page页为脏,那么其所在的访问路径会被标识成Dirty,而其他的路径不受影响。因此,文件系统在查询脏页的时候,会节省很多的检索时间。

2)将文件inode标识成dirty。通过__mark_inode_dirty函数将文件inode设置成脏,并且将该inode交给回写线程进行处理。通知回写线程的处理方式比较简单,直接将该inode挂载到writeback线程调度处理的inode链表中。我们知道,每个设备都会有一个writeback线程处理脏页回写过程,每个writeback都会维护一条需要处理的inode链表。当writeback线程被唤醒之后,其会从inode链表中获取需要处理的inode,并且从该inode所在的radix树中获取脏页,然后生成IO将数据写入磁盘。对于EXT3文件系统而言,其一定会架构在一个块设备之上,因此,在mount文件系统的时候,会将底层块设备的writeback对象(bdi)告诉给EXT3文件系统的root_inode。这样文件系统内部的inode需要进行回写数据时,直接将该inode设置成dirty,然后通过root_inode获取writeback,并且将需要处理的inode挂载到任务链表中,最后唤醒writeback线程进行数据回写操作。

EXT3所在设备的writeback线程被唤醒之后会将文件中的dirty page页刷新到磁盘。Linux中处理完成该功能的函数是do_writepageswriteback_single_inodeà do_writepages)。由于EXT3文件系统没有注册自己的writepages方法,因此,直接调用系统提供的generic_writepages处理脏页。刷新处理一个inodedirty page的核心函数是write_cache_pages。该函数的主要功能是搜索inode对应radix tree中的脏页,然后调用EXT3writepage方法将脏页中的数据同步到磁盘。在刷新数据的过程中与块设备层相接口的的方法是submit_bh。每个page页都会对应一个buffer headEXT3想要刷新数据的时候直接调用submit_bhpage页中生成若干个bio,然后转发至底层物理设备。至此,IO旅程从文件系统开始转向块设备层。

通过上述分析,我们可以了解到一个EXT3写操作基本可以分成如下两个步骤:

1)将数据写入page cache,每个文件都会采用一个radix treepage页进行高效检索管理。当数据被写入page页之后,需要将该页标识成dirty,说明该页中有新的数据需要刷新到磁盘。为了提高radix tree检索dirty page的性能,Linux采用了Tag机制。当page页被标识成dirty之后,需要将该inode加入到对应的writeback线程任务处理队列中,等待writeback线程调度处理。Radix treewriteback之间的关系如下图所示。

2)每个块设备都会有一个writeback内核线程处理page cache/buffer的回写任务。当该线程被调度后,会检索对应inoderadix tree,获取所有的脏页,然后调用块设备接口将脏数据回写到磁盘。

通过上述两大步骤,最常见的EXT3文件写操作完成。一个IO从用户态通过系统调用进入内核,然后在radix tree上小住了一段时间,即将踏上新的征程——块设备层。

 

<待续...>

转载地址:http://zjulx.baihongyu.com/

你可能感兴趣的文章
厕所老鼠和粮仓老鼠
查看>>
算法与数据结构:C语言的整数数组全排列(源码)
查看>>
arcengine,深入理解游标Cursors,实现数据的快速查找,插入,删除,更新
查看>>
Content-Script-Type的设置好象没多大用处,或许我不知道有什么用
查看>>
Linux 小知识翻译 - 「SCP和SFTP」
查看>>
漫画:面试过程的神回复
查看>>
Android开发之旅:HelloWorld项目的目录结构
查看>>
关于Java异常和错误的几个问题
查看>>
当流量获取不再简单,京东愈发重视大数据个性化推荐
查看>>
这24款汽车可以通过信号放大攻击被黑客盗走
查看>>
负面事件频发 智能终端隐现安全产业困境
查看>>
多孤岛数据访问加速初创公司Alluxio与Dell EMC签约合作
查看>>
数据称聊天应用现在已经比社交网络更受欢迎
查看>>
稳中有降 11月单路塔式服务器选购
查看>>
阿里张家口张北云联数据中心将于4月底运营
查看>>
"小狮子"荣光不复 瑞星信息去年亏损7300万元
查看>>
物流数据服务商G7获4500万美元C+轮融资,将扩展整条供应链场景
查看>>
能否在物联网领域构建一个通用的安全模型?
查看>>
【SQL Server】万事无忧——备份和恢复
查看>>
小议测试驱动开发
查看>>