综述

Page cache是通过将磁盘中的数据缓存到内存中,从而减少磁盘I/O操作,从而提高性能。此外,还要确保在page cache中的数据更改时能够被同步到磁盘上,后者被称为page回写(page writeback)。一个inode对应一个page cache对象,一个page cache对象包含多个物理page。

对磁盘的数据进行缓存从而提高性能主要是基于两个因素:第一,磁盘访问的速度比内存慢好几个数量级(毫秒和纳秒的差距)。第二是被访问过的数据,有很大概率会被再次访问。

Page Cache

Page cache由内存中的物理page组成,其内容对应磁盘上的block。page cache的大小是动态变化的,可以扩大,也可以在内存不足时缩小。cache缓存的存储设备被称为后备存储(backing store),注意我们在block I/O中提到的:一个page通常包含多个block,这些block不一定是连续的。

读Cache

当内核发起一个读请求时(例如进程发起read()请求),首先会检查请求的数据是否缓存到了page cache中,如果有,那么直接从内存中读取,不需要访问磁盘,这被称为cache命中(cache hit)。

如果cache中没有请求的数据,即cache未命中(cache miss),就必须从磁盘中读取数据。然后内核将读取的数据缓存到cache中,这样后续的读请求就可以命中cache了。page可以只缓存一个文件部分的内容,不需要把整个文件都缓存进来。

写Cache

当内核发起一个写请求时(例如进程发起write()请求),同样是直接往cache中写入,后备存储中的内容不会直接更新。内核会将被写入的page标记为dirty,并将其加入dirty list中。内核会周期性地将dirty list中的page写回到磁盘上,从而使磁盘上的数据和内存中缓存的数据一致。

Cache回收

Page cache的另一个重要工作是释放page,从而释放内存空间。cache回收的任务是选择合适的page释放,并且如果page是dirty的,需要将page写回到磁盘中再释放。理想的做法是释放距离下次访问时间最久的page,但是很明显,这是不现实的。下面先介绍LRU算法,然后介绍基于LRU改进的Two-List策略,后者是Linux使用的策略。

LRU算法

LRU(least rencently used)算法是选择最近一次访问时间最靠前的page,即干掉最近没被光顾过的page。原始LRU算法存在的问题是,有些文件只会被访问一次,但是按照LRU的算法,即使这些文件以后再也不会被访问了,但是如果它们是刚刚被访问的,就不会被选中。

Two-List策略

Two-List策略维护了两个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中,从而位置两个表的平衡。

Page Cache在Linux中的具体实现

address_space结构

内核使用address_space结构来表示一个page cache,address_space这个名字起得很糟糕,叫page_ache_entity可能更合适。下面是address_space的定义

struct address_space {struct inode            *host;              /* owning inode */struct radix_tree_root  page_tree;          /* radix tree of all pages */spinlock_t              tree_lock;          /* page_tree lock */unsigned int            i_mmap_writable;    /* VM_SHARED ma count */struct prio_tree_root   i_mmap;             /* list of all mappings */struct list_head        i_mmap_nonlinear;   /* VM_NONLINEAR ma list */spinlock_t              i_mmap_lock;        /* i_mmap lock */atomic_t                truncate_count;     /* truncate re count */unsigned long           nrpages;            /* total number of pages */pgoff_t                 writeback_index;    /* writeback start offset */struct address_space_operations *a_ops;     /* operations table */unsigned                long flags;         /* gfp_mask and error flags */struct backing_dev_info *backing_dev_info;  /* read-ahead information */spinlock_t              private_lock;       /* private lock */struct list_head        private_list;       /* private list */struct address_space    *assoc_mapping;     /* associated buffers */
};

其中 host域指向对应的inode对象,host有可能为NULL,这意味着这个address_space不是和一个文件关联,而是和swap area相关,swap是Linux中将匿名内存(比如进程的堆、栈等,没有一个文件作为back store)置换到swap area(比如swap分区)从而释放物理内存的一种机制。page_tree保存了该page cache中所有的page,使用基数树(radix Tree)来存储。i_mmap是保存了所有映射到当前page cache(物理的)的虚拟内存区域(VMA)。nrpages是当前address_space中page的数量。

address_space操作函数

address_space中的a_ops域指向操作函数表(struct address_space_operations),每个后备存储都要实现这个函数表,比如ext3文件系统在fs/ext3/inode.c中实现了这个函数表。

内核使用函数表中的函数管理page cache,其中最重要的两个函数是readpage() 和writepage()

readpage()函数

readpage()首先会调用find_get_page(mapping, index)在page cache中寻找请求的数据,mapping是要寻找的page cache对象,即address_space对象,index是要读取的数据在文件中的偏移量。如果请求的数据不在该page cache中,那么内核就会创建一个新的page加入page cache中,并将要请求的磁盘数据缓存到该page中,同时将page返回给调用者。

writepage() 函数

对于文件映射(host指向一个inode对象),page每次修改后都会调用SetPageDirty(page)将page标识为dirty。(个人理解swap映射的page不需要dirty,是因为不需要考虑断电丢失数据的问题,因为内存的数据断电时默认就是会失去的)内核首先在指定的address_space寻找目标page,如果没有,就分配一个page并加入到page cache中,然后内核发起一个写请求将数据从用户空间拷入内核空间,最后将数据写入磁盘中。(对从用户空间拷贝到内核空间不是很理解,后期会重点学习Linux读、写文件的详细过程然后写一篇详细的blog介绍)

Buffer Cache

在Block I/O的文章中提到用于表示内存到磁盘映射的buffer_head结构,每个buffer-block映射都有一个buffer_head结构,buffer_head中的b_assoc_map指向了address_space。在Linux2.4中,buffer cache和 page cache之间是独立的,前者使用老版本的buffer_head进行存储,这导致了一个磁盘block可能在两个cache中同时存在,造成了内存的浪费。2.6内核中将两者合并到了一起,使buffer_head只存储buffer-block的映射信息,不再存储block的内容。这样保证一个磁盘block在内存中只会有一个副本,减少了内存浪费。

Flusher线程群(Flusher Threads)

Page cache推迟了文件写入后备存储的时间,但是dirty page最终还是要被写回磁盘的。

内核在下面三种情况下会进行会将dirty page写回磁盘:

  • 用户进程调用sync() 和 fsync()系统调用
  • 空闲内存低于特定的阈值(threshold)
  • Dirty数据在内存中驻留的时间超过一个特定的阈值

线程群的特点是让一个线程负责一个存储设备(比如一个磁盘驱动器),多少个存储设备就用多少个线程。这样可以避免阻塞或者竞争的情况,提高效率。当空闲内存低于阈值时,内核就会调用wakeup_flusher_threads()来唤醒一个或者多个flusher线程,将数据写回磁盘。为了避免dirty数据在内存中驻留过长时间(避免在系统崩溃时丢失过多数据),内核会定期唤醒一个flusher线程,将驻留时间过长的dirty数据写回磁盘。

转载于:https://www.cnblogs.com/linhaostudy/p/10196915.html

Page Cache与Page回写相关推荐

  1. linux page cache 大小,Linux内核学习笔记(八)Page Cache与Page回写

    Page cache是通过将磁盘中的数据缓存到内存中,从而减少磁盘I/O操作,从而提高性能.此外,还要确保在page cache中的数据更改时能够被同步到磁盘上,后者被称为page回写(page wr ...

  2. 网络缓存 峰值 linux,Linux Page Cache调优在Kafka中的应用

    本文首发于 vivo互联网技术 微信公众号 链接: 作者:Yang Yijun 本文主要描述Linux Page Cache优化的背景.Page Cache的基本概念.列举之前针对Kafka的 IO ...

  3. Linux的Page Cache

    1. Page Cache 何为Page Cache 为了了解Page Cache我们可以看一下Linux的文件I/O系统 从图中可以看出,Page Cache是由Linux内核进行管理的,而且通过m ...

  4. Linux Page Cache参数调优在kafka中的应用

    文章目录 完整优化方案地址: 一.优化背景 二.基本概念 1.什么是Page Cache? 2.读Cache 3.写Cache 4.Page Cache缓存查看工具 三.参数调优 1.如何查看Page ...

  5. Linux读写缓存Page Cache

    一. 读写缓存Page Cache Linux对文件读写并不是每次都进行磁盘IO,而是将对应的磁盘文件缓存到内存上,之后对该文件的操作实际上也是对内存的读写. 缓存俗称页缓存(page cache), ...

  6. linux查看kafka状态_Linux Page Cache调优在Kafka中的应用

    本文主要描述Linux Page Cache优化的背景.Page Cache的基本概念.列举之前针对Kafka的 IO 性能瓶颈采取的一些解决方案.如何进行Page Cache相关参数调整以及性能优化 ...

  7. 和linux关系_Linux内核Page Cache和Buffer Cache关系及演化历史

    在我们进行数据持久化,对文件内容进行落盘处理时,我们时常会使用fsync操作,该操作会将文件关联的脏页(dirty page)数据(实际文件内容及元数据信息)一同写回磁盘.这里提到的脏页(dirty ...

  8. linux刷新磁盘_Linux内核Page Cache和Buffer Cache关系及演化历史

    在我们进行数据持久化,对文件内容进行落盘处理时,我们时常会使用fsync操作,该操作会将文件关联的脏页(dirty page)数据(实际文件内容及元数据信息)一同写回磁盘.这里提到的脏页(dirty ...

  9. linux那些事之page cache

    page cache page cache又称高速缓存,主要是针对文件文件系统,为了减少不必要的磁盘IO操作(读/写)造成卡顿问题,内核将磁盘文件中的内容缓存到内存中,并选择适当时机对磁盘进行读写操作 ...

  10. Page Cache:为什么我的容器内存使用量总是在临界点?

    Memory Cgroup 是如何控制一个容器的内存的.我们已经知道了,如果容器使用的物理内存超过了 Memory Cgroup 里的 memory.limit_in_bytes 值,那么容器中的进程 ...

最新文章

  1. MATLAB常见语法错误分析及解决办法
  2. 非锐化掩膜与高提升滤波
  3. Mysql Mariadb 创建新用户
  4. 《降级论》《按时交作业的学生何以常穿脏袜子》读后感
  5. 10分钟理解依赖注入
  6. Python中的遇到的错误(持续更新)
  7. 语音识别技术发展史与行业最佳实践全解析
  8. 利用Onionshare 共享匿名文件
  9. python百度关键词爬虫_python批量获取百度关键词相关结果数
  10. 免费杀软中的王者:德国小红伞评测
  11. swfobject简单封装
  12. 学习阶段小测,简单的幸运抽奖系统
  13. 解决Ubuntu中Edge浏览器smartUp手势插件导致鼠标右键不灵敏的问题
  14. 炉石传说一直显示重新连接服务器,炉石传说一直显示战网开启中 炉石传说卡在启动战网解决办法...
  15. CCF 201604-2 俄罗斯方块
  16. 送给计算机老师平安夜贺卡,给老师的平安夜温馨祝福语
  17. 利用百度地图API接口自制地图
  18. 生物+化学 SY000 实验传闻记录 简述(暂记)
  19. KL变换生成人脸特征图
  20. 【计算机毕业设计】java jsp+ssm大学校园宿舍零食便利店系统

热门文章

  1. FQDN(Fully qualified domain name)
  2. SSO单点登录系列1:cas客户端源码分析cas-client-java-2.1.1.jar
  3. spring.net学习(一) 搭建环境,实例化spring.net容器。
  4. 『C#基础』C#调用存储过程
  5. 【博客园IT新闻】博客园IT新闻 iPhone 客户端发布
  6. 撸了一个疫苗接种行程管理系统,爽!
  7. Google 推荐和鼓励的 13 条代码审查标准,建议收藏!
  8. Lambda 表达式入门,这篇够了!
  9. 人生的要务:提高自己的思维和格局
  10. IBM推出世界最小电脑,应用区块链技术防偷骗!