文件读I/O流程与Cache机制和Cache回收-爱代码爱编程
一 读文件过程
用户进程读取文件数据,有两种情形:
1 所需要的数据不在内存中,也即不在页面Cache中。这时就需要直接从磁盘上读取;
2 所需的数据已经在内存中,此时只需从页面Cache中找到具体位置,然后将数据拷贝到用户缓冲区。而不需要进行磁盘I/O操作。
1.1 数据读入页面Cache中
在不是DIRECT_IO的情况下,系统调用read执行到函数generic_file_buffered_read(文件mm/filemap.c中)下面通过流程图来解析generic_file_buffered_read()的源码:
请求页不在pagecache中的情况:
1) 首先通过find_get_page查找请求数据是否已经在页面Cache中,在Cache找不到, 就调用page_cache_sync_readahead将数据预读到内存中;
2)如果预读数据到内存页也失败,调用page_cache_alloc申请内存页,失败则返回为内存不足的错误码,如果申请成功则将page添加到LRU活跃链表中去;
3)如果添加过程中发现该申请页已经在LRU队列中,可能其他进程已经提前访问了该页,
通过find_get_page再次查找页位置;如果不在LRU队列中,则调用文件系统readpage成员函数将数据从磁盘读入到内存,通过copy_page_to_iter将数据从内存拷贝到用户缓存区域。
请求页在pagecache的情况:
1) 用page_cache_sync_readahead将数据预读到内存中,检查数据是否上传完毕;
2) 如果上传完毕则通过copy_page_to_iter将数据从内存拷贝到用户缓存区域;
3)数据未上传完毕则通过wait_on_page_locked_killable等待数据上传完毕,最后通过copy_page_to_iter将数据从内存拷贝到用户缓存区域。
注意:不管用户请求的数据是否已经在pagecache中,都会从页面pagecache中查找相应的页。也就是读文件的过程就是先从磁盘读数据到pagecache,再从pagecache拷贝到用户缓冲区中。
至此,分析了数据不在/在pagecache中的两种读情形,但Linux内核文件Cache机制,是主要提高性能,这样进程读取数据时,主要是已经在读pagecache中的数据,只有这样性能才会显著提升。
二 LRU回收机制和cache回收
在系统中cache回收遵循LRU的内存回收机制,在理解cache回收处理前先要理解LRU的回收
思想。
2.1 LRU的回收机制
LRU(least rencently used)算法是选择最近一次不活跃列表中最靠后的page,即删除最近没被光顾过的page。策略维护了两个list,active list 和 inactive list。在active list上的page被认为是hot的,不能释放。只有inactive list上的page可以被释放的。首次缓存的数据的page会被加入到inactive list中,已经在inactive list中的page如果再次被访问,就会移入active list中。两个链表都使用了LRU算法维护,新的page从尾部加入,移除时从头部移除,就像队列一样。如果active list中page的数量远大于inactive list,那么active list头部的页面会被移入inactive list中,从而位置两个表的平衡。
对应各种类型页的标志位
每条LRU链表中的物理页按访问时间从大到小排序,链表首部的物理页的访问时间离当前最近,物理页从LRU链表的首部加入,页回收算法从不活动LRU链表的尾部取物理页回收,从活动LRU链表的尾部取物理页移动到不活动链表中。
2.2 页回收的触发
申请分配页的时候,页分配器首先尝试使用低水线分配页。如果使用低水线分配失败,说明内存轻微不足,页分配器将会唤醒所有符合分配条件的内存节点的页回收线程,异步回收,然后尝试使用低水线分配页。如果分配失败,说明内存严重不足,页分配器将会直接回收页。如果直接回收页失败,那么判断是否需要重新尝试回收页。
NUMA架构的处理器中,内存被划分为多个内存节点,访问一个内存的时间取决于处理器和内存节点的距离。
以麒麟v10系统为例,关于numa内存节点的优选顺序可以通过:
sysctl -a |grep vm.numa_zonelist_order 查看。默认为Node模式根据节点距离从小到大排序,这种配置保证了内存的访问效率。下图是某项目中dm数据库服务器系统numa zone内node0的NORMAL内存状态;
Free:节点内可用的内存数量;
Min:最低水线,free小于min,内存区域严重不足;
Low: 低水线;free小于low大于min,内存轻微不足;
High:高水线;free大于high,内存充足。
通过上图可知free已经小于最低水线,说明node0节点内存严重不足,同时会从备用的swap分区借用物理内存,这也是使用swap分区的原因,将类似于匿名页的数据保存在交换分区,如果交换分区的页被访问会再次读到内存中。图中也统计了anonpage,filepage的活跃非活跃页的占用数量。
2.3 pagecahe回收
无论是页回收线程去回收内存还是直接回收内存的方式去回收内存都会调用shrink_node去回收LRU链表中的页,简单流程图如下:
所以对于pagecache回收而言,要满足以下条件:
- pagecache是否处于不活跃的状态,即没有PG_refrenced标志;
- 在非活动链表中,pagecache对应的refcount(访问数量)为0;
- pagecache没有设置内存锁。
如果不满足以上条件文件缓存依旧会占用物理内存,不会被内核主动回收。