Linux IO系统的脏页写入的到磁盘,主要由以下情况来触发:
1. 内存不足或内存空间紧张,需要通过回写脏页来回收内存;
2. 脏页已经更新了较长时间,时间上已经到了临界值,需要及时回写保持内存和磁盘上数据一致性;
3. 第三方英语或命令主动触发脏页回刷到磁盘;
4. write操作时balance回写要求。
当linux系统内存压力就大时,就会对系统的每个压力大的zone进程内存回收,内存回收主要是针对匿名页和文件页进行的。
在page cache创建过程,经常使用free_more_memory函数调用wakeup_flusher_threads来唤醒回写线程进行内存回收。
/* * Kick thewriteback threads then try to free up some ZONE_NORMAL memory. */ static void free_more_memory(void) { structzone *zone; int nid; /* 启动回刷线程,要求至少回收1024个page,回收原因 Freemore memory */ wakeup_flusher_threads(1024,WB_REASON_FREE_MORE_MEM); yield(); /* 遍历node,尝试通过直接回收page来达到要求 */ for_each_online_node(nid){ (void)first_zones_zonelist(node_zonelist(nid,GFP_NOFS), gfp_zone(GFP_NOFS),NULL, &zone); if(zone) try_to_free_pages(node_zonelist(nid,GFP_NOFS), 0, GFP_NOFS,NULL); } }
内核在启动的时候会进行页回写机制初始化:start_kernel-> page_writeback_init,在page_writeback_init初始化dom->period_timer定时器:
void __init page_writeback_init(void) { BUG_ON(wb_domain_init(&global_wb_domain,GFP_KERNEL)); writeback_set_ratelimit(); register_cpu_notifier(&ratelimit_nb); }
超时时间是dirty_writeback_centisecs,单位是0.01秒,可以通过/proc/sys/vm/dirty_writeback_centisecs调节。wb_timer的触发函数是wb_timer_fn,最终是通过wb_workfn实现。
外界触发式,主要是调用sync等函数,来触发实现。参考文档《linux 同步IO: sync、fsync与fdatasync》
主要调用
static int __sync_filesystem(struct super_block*sb, int wait) { if (wait) sync_inodes_sb(sb); else writeback_inodes_sb(sb,WB_REASON_SYNC); /* writeback回写机制 */ if(sb->s_op->sync_fs) sb->s_op->sync_fs(sb,wait); return__sync_blockdev(sb->s_bdev, wait); }
用户态使用WRITE函数写文件时也有可能要刷新脏页,generic_perform_write函数会在将写的内存页标记为脏之后,根据条件刷新磁盘以平衡当前脏页比率,参看balance_dirty_pages_ratelimited函数。
balance_dirty_pages_ratelimited函数通过ratelimit_pages调节刷新(调用balance_dirty_pages函数)的次数,每ratelimit_pages次调用才会刷新一次,具体刷新过程看balance_dirty_pages函数。
balance_dirty_page函数走进一个死循环,通过get_dirty_limits获取dirty_background_ratio和dirty_ratio对应的内存页数值,判断,如果脏页大于dirty_thresh,则调用writeback_inodes开始刷缓存到磁盘,如果一次没有将脏页比率刷到dirty_ratio之下,则用blk_congestion_wait阻塞写,然后反复循环,直到比率降低到dirty_ratio;当比率低于dirty_ratio之后,但脏页比率大于dirty_background_ratio,则用pdflush_operation启用background_writeout,pdflush_operation是非阻塞函数,唤醒pdflush后直接返回,background_writeout在有pdflush调用。
如此可知:WRITE写的时候,缓存超过dirty_ratio,则会阻塞写操作,回刷脏页,直到缓存低于dirty_ratio;如果缓存高于background_writeout,则会在写操作时,唤醒回写进程刷脏页,不阻塞写操作。