buffer和cache使用率降低的方法(page cache)


请尊重原创版权,转载注明出处。

本文基于内核4.2

问题描述

    长时间运行的服务容易出现cache或者buffer占用内存过多的问题; 另外,在嵌入式设备中,内存相对较少,如果出现大量数据的拷贝,很容易出现拷贝进程被oops杀死的情况。

    cache:内核用于读优化。简而言之,我们调取read函数, 内核会先查找cache中是否存在该数据,如果命中,那么read系统调用将直接拷贝cache中的数据; 如果没有命中,内核再将数据从存储介质中读出,放入cache。

    buffer:内核用于写优化。 数据先写入page cache,然后直接返回;由后备存储线程负责数据的刷回操作。

注:用户空间看到的buffer和cache对内核而言都是page cache,即address_space;只不过操作函数和使用目的不同而已。

具体操作

读操作

    读取的过程可以参考这里 中的最后一幅流程图部分

    在流程图中是裸块设备操作, 因此涉及到的操作都在fs/block_dev.c中,vfs_read调用的f->op->read操作为blkdev_read_iter (因为f_op->read在block文件系统中未定义)

const struct file_operations def_blk_fops = {
    ……
	.read_iter	= blkdev_read_iter,
	.write_iter	= blkdev_write_iter,
    ……
}; 

    在blkdev_read_iter之后,除去一些访问的检查,最终将调用do_generic_file_read。

    do_generic_file_read实现查找cache的缓存,即利用基数树进行查找; 正如之前所言,这里会产生两条分支:

  • 未找到相应的page cache,那么调用readpage,将数据读入缓存;这里的操作大同小异, 最终目的都是将page cache转换成bio,加入到设备的I/O请求队列中。
  • 找到相应的page cache,则将数据拷贝至用户空间copy_page_to_iter

注:在调用readpage时,page页面已经被lock(lock_page_killable);在提交io之后, PageUptodate将判断页面是否被读出,如果仍未读出,将再调用lock_page_killable,使读操作进入阻塞。

    PageUptodate的尝试次数将有三次。若三次均未被读出,那么返回失败。

static const struct address_space_operations def_blk_aops = {
	.readpage	= blkdev_readpage,
	.readpages	= blkdev_readpages,
	.writepage	= blkdev_writepage,
	.write_begin	= blkdev_write_begin,
	.write_end	= blkdev_write_end,
	.writepages	= generic_writepages,
    ……
};

    以上是读操作的大致过程。

写操作

    与读类似,写操作调用__generic_file_write_iter,如果是bio, 那么该函数会调用generic_perform_write,该函数中会调用page cache的方法write_begin和writepages, 以及balance_dirty_pages_ratelimited。

    最终数据是否同步由函数balance_dirty_pages_ratelimited决定, 该函数适用于所有的文件系统。

    balance_dirty_pages_ratelimited中, 其主要函数为balance_dirty_pages。该函数主要涉及以下结构体, 该结构体控制了page cache刷回的阈值。

struct dirty_throttle_control {
#ifdef CONFIG_CGROUP_WRITEBACK
	struct wb_domain	*dom;
	struct dirty_throttle_control *gdtc;	/* only set in memcg dtc's */
#endif
	struct bdi_writeback	*wb;
	struct fprop_local_percpu *wb_completions;
 
	unsigned long		avail;		/* dirtyable */
	unsigned long		dirty;		/* file_dirty + write + nfs */
	unsigned long		thresh;		/* dirty threshold */
	unsigned long		bg_thresh;	/* dirty background threshold */
 
	unsigned long		wb_dirty;	/* per-wb counterparts */
	unsigned long		wb_thresh;
	unsigned long		wb_bg_thresh;
 
	unsigned long		pos_ratio;
};

方法

    (待完整)     

文档信息
--------------
* 版权声明:自由转载-非商用
* 转载: [buffercache使用率降低的方法(page cache]

buffer和cache使用率降低的方法(page cache)