read()/write()的生命旅程之四——第四章:writeback

本文深入探讨了Linux文件系统中的Writeback机制,详细介绍了如何通过writebackthread将内存中的脏页面写回到磁盘,并讨论了writeback机制如何提高写操作效率及优化磁盘访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第三章write()中已经提到要写的page加到了writeback queue后将有writeback thread将其真正写到block device上,而不是每一次写都直接写到media上。如果要绕过writeback机制直接写到media上,在open()的时候指定O_DIRECT即可。Writeback机制的好处总结起来主要是两点:
  • 加快write()的响应速度。因为media的读写相对于内存访问是较慢的。如果每个write()都访问media,势必很慢。将较慢的media访问交给writeback thread,而write()本身的thread里只在内存里操作数据,将数据交到writeback queue即返回。
  • 便于合并和排序 (merge and sort) 多个write,merge是将多个少量数据的write合并成几个大量数据的write,减少访问media的次数;sort是将无序的write按照其访问media上的block的顺序排序,减少磁头在media上的移动距离。

下面将说明writeback的活动机制。

  • 概念:writeback和bdi
从上面的阐述writeback的概念里可以看 出,writeback是文件系统的概念,writeback是指文件 (即inode)的部分数据由writeback thread写入medi a。这里就产生一个问题,一个media上可能有多个文件系统,如sda1上是ext4, sda2上是fat,但其实是在一个硬盘sda上,如果分别由不同的thread对sda1,sda2进行writeback的话,上面所说的排序(sort)功能将没有意义,因为可能thread1写了一会儿sda1,thread2又要把磁头移到别的地方去写sda2了。鉴于此,设计writeback机制时,writeback的主体应该是sda,而不是sda1, sda2。这就是backing device的概念。sda是sda1上ext4, sda2上fat的backing device,sda1和sda2的writeback都应该由backing device:sda的writeback thread来完成。
   bdi的全称是backing device info。它代表了一个backing device。每个文件系统在mount的时候,它的backing device就被记录到super->s_bdi中。如果backing device是个block device,那么其inode所在的backing device被记录到super->s_bdev->bd_inode->i_mapping->backing_dev_info中。Super和文件数据可以在不同的backing device上。对于block device,在block device创建的同时就创建了一个backing device info,基于该block device的文件系统在mount的时候将该backing device info链到自己的super和 inode的backing_dev_info。对于不在block device上的文件系统,如 FUSE,NFS等,是在mount的时候创建一个单独的backing device info ,这个backing device并不是一个实际的device,只是一个虚拟的writeback的主体。此外,在init的时候,会创建一个default backing device info,文件系统可以不必创建自己单独的 backing device info,直接使用default backing device info
      下面这张图全面展示了writeback的工作机制,接下来的章节我们将逐一讲述。

  • bdi的init, register
上节已经讲过default bdi在sys init时创建,而每个block device在创建的时候或每个文件系统在mount的时候可以创建自己单独的bdi。创建一个bdi主要包含以下步骤:

(1). 在bdi_init中:

   *  创建一个struct backing_dev_info的变量来代表一个bdi。

   *  创建一个work queue: bdi_wq用于存放writeback的事件。

   * 创建一个delayed work(即bdi->wb.dwork)的thread,这个thread负责完成writeback动作。

(2). 在bdi_register中:

   * 将这个bdi链接到全局变量bdi_list所指的bdi list里


default bdi和其他bdi的创建都是遵循了以上四个步骤,下面分别讲述。
  • default bdi的init, register
下图是在kernel init时default_bdi_init的流程,default bdi由default_backing_dev_info这个全局变量来代表。除了default_backing_dev_info之外,还创建了一个noop_backing_dev_info,它用来代表没有writeback需要。

  • block device (scsi disk) bdi的init, register
Block device的bdi创建过程和default bdi一样,只是backing dev info是在创建sda的request queue的时候同时创建的。请注意backing dev info是跟着sda的,不是sda1, sda2。

  • 从writeback queue到bio
  • Writeback thread
                     在第三章write()里面我们已经提到在 __mark_inode_diry()里dirty的inode被移到writeback queue: bdi->wb.b_dirty里。这里我们将说明writeback queue的dirty inode的dirty page是怎样变成bio的。
首先,我们从writeback thread启动开始。从前面的图中我们已经知道,writeback thread是在bdi_init()时创建的一个delayed work,即bdiX->wb.dwork。当bdiX->wb.dwork->timer expire时,writeback thread的函数bdi_writeback_workfn()就开始运行,它的流程如下:

注意writeback的优先级顺序,先是强制writeback的work,如用户执行了fsync(fd),然后是writeback interval到了之后的work,最后是background的work。'work'是writeback的‘单位’,不管是sync()还是dirty_writeback_interval时间到了的writeback,都要包装成一个'work'交给writeback thread。
  • 一个'work'的Writeback过程

wb_writeback()函数负责完成一个work的writeback,下图是其流程。


 一般一个文件系统mount的时候会有一个super block,对于文件系统层面的sync产生的writeback work,它的super block即work->sb是确定的。对于dirty_writeback_interval时间到了产生的work,所有在该bdi上的super block的dirty inode都要writeback,work->sb就不能指定了。典型的场景是sda1上有一个文件系统,sda2上有另一个文件系统,它们只有一个bdi。当只sync sda1的时候,产生的work->sb指定为sda1的super block,调用writeback_sb_inode()。当interval时间到了,sda bdi上所有的dirty inode都要sync,此时产生的work->sb就不能指定了,调用__writeback_inodes_wb()。实际上在__writeback_inodes_wb()内部还是遍历每个inode,软后调用writeback_sb_inode()的。所以writeback_sb_inode()是核心函数,下图是它的流程,因为比较长,我画了两页图。简单的说,就是一个一个writeback dirty inode,由__writeback_single_inode() 完成,直到这次writeback的时间到了或者没有dirty inode了。wbc是writeback control,用来控制writeback的。

writeback_sb_inode()的流程(图1):

 

writeback_sb_inode()的流程(图2, 接图1):


下面是__writeback_single_inode()的流程,其本质是通过实际文件系统实现时注册的address_space_operations的writebackpages() 或writbackpage()将dirty的Page写到media上,如果inode本身也是dirty的(比如文件的meta data, size等也改变了),那么这些文件信息也要写回media。

 


下面是ext2里aops->writeback的流程,本质是在实际文件系统的格式里找到要写page对应的block位置,构建出相应的buffer head(bh)以及bio,然后调用submit_bio()来产生一个对block device的write。


 

至此,我们已经讲完了dirty inode从writeback queue到bio的过程。bio还只是一个抽象的读写block device的请求,bio要变成实际的block device access还要通过block device driver,还要再排队,还要受到ioschduluer的控制。这些将在第五章讲述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值