分类: LINUX
2011-01-26 18:35:50
函数sync_inodes遍历每个超级块的脏节点链表,把节点写回到块设备,并等待回写操作的完成,写完成后把节点放回到正常的链表中。函数sync_inodes是给系统调用sys_sync用的,函数fsync_dev使用同样的算法。sync函数的精细地方是块设备"超级块"最后被处理。这是因为函数write_inode是典型的文件系统操作函数,它不执行I/O,而是在块设备映射的地址空间把buffer标识为脏。我们所想做的是先执行所有的标识脏的操作,接着,在一次扫描中通过块设备映射地址空间写回所有的节点块。这样附加的(在某种程度上说是冗余的)sync_blockdev函数在这儿调用来确认这个操作真正发生。因为如果我们对明显的脏节点调用函数sync_inodes_sb(wait=1),回写操作将在文件系统的函数write_inode里有时进入堵塞,这种情况下将运行极慢。
函数sync_inodes的调用层次图如上图,函数sync_inodes分析如下(在fs/fs-writeback.c中):函数sync_inodes_sb执行回写操作并等待在文件系统的脏节点上。调用者将有两个方式调用这个函数,一个是写,一个是等待写。对于等待写方式来说,WB_SYNC_HOLD标识用来把写的节点停在sb->s_dirty上等待。为了防止函数sys_sync的死锁,将写的页数给了一个限制。限制被加到潜在的脏节点里,因为每个节点写操作能弄脏块设备的页缓存。
如果是一个pdlfush线程,就对整个链表使用pdflush的防冲突措施。对于函数sys_sync来说,WB_SYNC_HOLD标识是一个hack,它重绑定inode到sb->s_dirty,以便inode能被在__writeback_single_inode()上的线程定位。
函数sync_sb_inodes应在inode_lock情况下被调用。如果bdi结构非空,表示我们正被请求回写一个特定的队列,这个函数假定块设备超级块的节点被各种队列支持。因而所有的节点被搜索。对其它超级块来说,假定所有的节点被同一队列支持。
被写的节点被停在sb->s_io上,当他们被选择写时,它们被移回到sb->s_dirty上。这样,在写者控制的途中就不会有丢失。并且得到在多个调节线程之间的相当好的平衡:我们不想它们所有的堆积在__wait_on_inode上。
mapping: 要写的地址空间结构
wbc: 回写所有页的信息结构,从wbc->nr_to_write 减去已写的页数
get_block: 文件系统的块映射函数指针,如果这是NULL,那么使用 a_ops->writepage,否则使用direct-to-BIO。
函数mpage_writepages是一个库函数,它利用地址空间结构里操作函数writepages()。如果一页已在I/O,使用generic_writepages()跳过它,即使它是脏的。对于内存清理的回写操作来说这是理想的行为,但它对调用如fsync()来进行数据一致性同步来说是不正确的。fsync()和msync()需要保证脏数据得到针对它们已开始的新I/O,所有的这些脏数据在调用时必须已准备好。如果wbc->sync_mode是WB_SYNC_ALL,那么函数mpage_writepages被调用来进行数据一致性回写,并且它必须等待正存在的I/O完成。
函数sync_blockdev通过块设备的设备节点的地址空间,回写并等待所有的与一个块设备相关的脏数据写回到设备上。函数sync_blockdev的调用层次图如上图。