页缓存回写时,导致sync阻塞的问题
问题抽象出来是这样: 有一块ubifs文件格式的flash,挂在/mtd3上 拷贝一个大文件big_flash(大约40M),到/mtd3上 如果在拷贝文件的时候,执行sync命令,则会导致sync长时间阻塞,阻塞链为: wait_on_bit(WS_USED_B) bdi_wait_on_work_clear bdi_sync_writeback(sb) sync_inodes_sb(sb) __sync_filesystem sync_filesystems 其中,关键代码为线程将自己挂载到新初始化的work等待队列里,等待flusher处理。 bdi_queue_work list_add_tail_rcu(&work->list,&bdi->work_list); wake_up_process(wb->task); 可以看出,sync将本刷新请求封装到work里,挂到bdi的work_list链表,然后唤醒flusher,尝试处理这些回写。 因为flusher处理不及时,导致sync长时间阻塞。 于是,就要找出问题时,flusher线程在做什么。 猜测如下场景: 1) cp命令,在big_flash的inode里的mapping页缓存里,产生大量脏页,flusher线程处理这些脏页的回写, ? ? ? 以每次1024个页面的规模,往flash写数据。疑问:这个flusher正在处理的work是如何生成的。 2)当刷出的page cache数目达到系统水线后,不再继续刷此work代表的page cache,退出返回上一层后,唤醒处于等待此刷新work ? ? ?的进程 ? ?3)继续取下一个work进行处理。这个work正好就是sync提交的刷页请求。在最终调用pagevec_lookup_tag 找不到更多的脏页后, ? ?退出此work处理,唤醒sync线程 所以问题还是在于1步骤太耗时导致的。 循环拷贝文件时, 执行sync一直不能退出问题测试分析结论如下: 原理及知识要点: 1) sync: 同步等待所有脏页刷入 flash 2) 内核flusher线程,负责将脏页刷入 flash后,通知1完成 3) 测试用例,循环拷贝,删除文件,不断产生脏页 4) 大部分flash文件系统,采取的是异地更新策略。即不修改当前数据,而重新拷贝一份,修改好拷贝的数 据后,删除原来的废弃数据。 ? ?? 5)在出问题时,2的速度,小于3的速度,导致1完成不了。 ? ? 出问题时,发现flusher一直在处理sync提交的刷新任务(即sync阻塞等待的任务) 为什么会一直处理sync的任务? 因为,sync提交的任务是同步刷新,即带有WB_SYNC_ALL标志。这个标志的意思是,等待bdi设备里的所有脏页 刷完为止。 具体的内核实现点在write_cache_pages函数,这个函数本身是个大循环: int write_cache_pages(struct address_space *mapping,? ? ?struct writeback_control *wbc,writepage_t writepage,? ? ?void *data) { ... while (!done && (index <= end)) { int i; nr_pages = pagevec_lookup_tag(&pvec,mapping,&index,? ? ?PAGECACHE_TAG_DIRTY,? ? ?min(end - index,(pgoff_t)PAGEVEC_SIZE-1) + 1); ? ? ? ? ? ? //每次尝试从inode的mapping里取出14个脏页,刷到flash里,如果这个inode没有脏页了,就停 止刷新 if (nr_pages == 0) break; ? ? ? ? ... ? ? ? ? ? ? ?for (i = 0; i < nr_pages; i++) { ... ret = (*writepage)(page,wbc,data); ? ? ? ? ? ? ? ? ? ? ? if (nr_to_write > 0) { nr_to_write--; ? ? ? ? ? ? ? ? ? ? ? ?if (nr_to_write == 0 && ? ?wbc->sync_mode == WB_SYNC_NONE) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? //非同步方式的刷新,当回写1024个页面后,就退出不再回写,但我们是 WB_SYNC_ALL,因此会一直遍历inode的脏页 ? ? ? ? ? ? ? ? ? ? ? ? ? //直到没有脏页可写为止。 ? ? ? ? ? ? ? ? ? ? ? ? ?//由于ubifs(其实所有异地更新文件系统都有这个问题),在修改文件过程中, ? ? ? ? ? ? ? ? ? ? ? ? ?//会产生大量无效的物理块,如果不及时回收,则ubi写flash会非常慢。 ? ? ? ? ? ? ? ? ? ? ? ? ? //就算最后,终于刷完从index=0后的所有脏页,由于此时一定有nr_to_write =? 0,即已经刷完了1024个页面,返回到外层函数 ? ? ? ? ? ? ? ? ? ? ? ? ? ?//wb_writeback后,还会再次进入write_cache_pages尝试从index = 0刷1024 个页, ? ? ? ? ? ? ? ? ? ? ? ? ? //因此,造成不能退出的位置在 wb_writeback函数的循环里 ? ?done = 1; ? ?break; ? ? ? ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } } } static long wb_writeback(struct bdi_writeback *wb,struct wb_writeback_args *args) { ? ?for (;;) { ? ? ? ? ? ? writeback_inodes_wb(wb,&wbc); ? ? ? ? ?if (wbc.nr_to_write <= 0) ?//这里无法退出到上层,不能设置work处理完毕,也就不能 通知sync线程刷新完成 continue; ? ?} } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |