Linux页快速缓存与回写机制分析
參考
《Linux内核设计与实现》
*******************************************
页快速缓存是linux内核实现的一种主要磁盘缓存,它主要用来降低对磁盘的IO操作,详细地讲,是通过把磁盘中的数据缓存到物理内存中,把对磁盘的訪问变为对物理内存的訪问。为什么要这么做呢?一,速度;二暂时局部原理。有关这两个概念,相信熟悉操作系统的我们不会太陌生。页快速缓存是由RAM中的物理页组成的,缓存中的每一页都相应着磁盘中的多个块。每当内核開始运行一个页IO操作时,就先到快速缓存中找。这样就能够大大降低磁盘操作。
一个物理页可能由多个不连续的物理磁盘块组成。也正是因为页面中映射的磁盘块不一定连续,所以在页快速缓存中检測特定数据是否已被缓存就变得不那么easy了。另外linux页快速缓存对被缓存页的范围定义的很宽。缓存的目标是不论什么基于页的对象,这包括各种类型的文件和各种类型的内存映射。为了满足普遍性要求,linux使用定义在linux/fs.h中的结构体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 */
};
当中的i_mmap字段是一个优先搜索树,它的搜索范围包括了在address_sapce中私有的和共享的页面。nrpages反应了address_space空间的大小。address_space结构往往会和某些内核对象关联。通常情况下,会与一个索引节点(inode)关联,这时host域就会指向该索引节点。假设关联对象不是一个索引节点的话,比方address_space和swapper关联时,这是host域会被置为NULL。a_ops域指向地址空间对象中的操作函数表,这与VFS对象及其操作函数表关系类似,操作函数表定义在linux/fs.h中,由address_space_operations表示,例如以下:
struct address_space_operations {
int (*writepage)(struct page *, struct writeback_control *);
int (*readpage) (struct file *, struct page *);
int (*sync_page) (struct page *);
int (*writepages) (struct address_space *, struct writeback_control *);
int (*set_page_dirty) (struct page *);
int (*readpages) (struct file *, struct address_space *,struct list_head *, unsigned);
int (*prepare_write) (struct file *, struct page *, unsigned, unsigned);
int (*commit_write) (struct file *, struct page *, unsigned, unsigned);
sector_t (*bmap)(struct address_space *, sector_t);
int (*invalidatepage) (struct page *, unsigned long);
int (*releasepage) (struct page *, int);
int (*direct_IO) (int, struct kiocb *, const struct iovec *,loff_t, unsigned long);
};
background-color: rgb(255, 255, 255);">这里面最重要的两个就是readpage()与writepage()了。对于readpage()方法而言,首先,一个address_space对象和一个偏移量会被传给该方法,这两个參数用来在页快速缓存中搜素须要的数据:
page = find_get_page(mapping, index); mapping是指定的地址空间,index是文件里的指定位置。假设要搜索的页并没在快速缓存中,那么内核将分配一个新页面,然后将其增加到页快速缓存中,例如以下
int error; cached_page = page_cache_alloc_cold(mapping);
if (!cached_page)
/* error allocating memory */
error = add_to_page_cache_lru(cached_page, mapping, index, GFP_KERNEL);
if (error)
/* error adding page to page cache */
最后,须要的数据从磁盘被读入,再被增加页快速缓存,然后返回给用户:error = mapping->a_ops->readpage(file,page);
写操作和读操作有少许不同。对于文件映射来说,当页被改动了,VM只须要调用:setPageDirty(page);内核晚些时候通过writepage()方法把页写出。对特定文件的写操作会比較复杂----它的代码在文件mm/filemap.c中,通常写操作路径基本上要包括一下各步:
page = __grab_cache_page(mapping, index, &cached_page, &lru_pvec);
status = a_ops->prepare_write(file, page, offset, offset+bytes);
page_fault = filemap_copy_from_user(page, offset, buf, bytes);
status = a_ops->commit_write(file, page, offset, offset+bytes);
首先,在页快速缓存中搜索须要的页,假设须要的页不在快速缓存中,那么内核在快速缓存中新分配一空暇项;下一步,prepare_write()方法被调用,创建一个写请求;接着数据被从用户空间复制到内核缓冲;最后通过commit_write()函数将数据写入磁盘。
由于在不论什么页IO操作前内核都要检查页是否已经在页快速缓存中了,所以这样的检查必须迅速,高效。否则得不偿失了。前边已经说过,也快速缓存通过两个參数address_space对象和一个偏移量进行搜索。每一个address_space对象都有唯一的基树(radix tree),它保证在page_tree结构体中。基树是一个二叉树,仅仅要指定了文件偏移量,就能够在基树中迅速检索到希望的数据,页快速缓存的搜索函数find_get_
page()要调用函数radix_tree_lookup(),该函数会在指定基树中搜索指定页面。基树核心代码的通用形式能够在文件lib/radix-tree.c中找到,另外想要使用基树,须要包括头文件linux/radix_tree.h.
在内存中累积起来的脏页必须被写回到磁盘,在一下两种情况下,脏页会被写会到磁盘:
1.在空暇内存低于一个特定的阈值时,内核必须将脏页写回磁盘,以便释放内存。
2.当脏页在内存中驻留超过一定的阈值时,内核必须将超时的脏页写会磁盘,以确保脏页不会无限期地驻留在内存中。
如今你仅仅需知道,2.6内核中,使用pdflush后台回写例程来完毕这个工作。那么详细是怎么实现的呢:
首先,pdflush线程在系统中的空暇内存低于一个特定的阈值时,将脏页刷新回磁盘。该后台回写例程的目的在于在可用物理内存过低时,释放脏页以又一次获得内存。上面提到的特定的内存阈值能够通过dirty_background_ratio系统调用设置。一旦空暇内存比这个指小时,内核便会调用函数wakeup_bdflush() 唤醒一个pdflush线程,随后pdflush线程进一步调用函数background_writeout()開始将脏页写会到磁盘,函数background_writeout()须要一个长整型參数,该參数指定试图写回的页面数目。函数background_writeout会连续地写会数据,直到满足一下两个条件:
1.已经有指定的最小数目的页被写回到磁盘。
2.空暇内存页已经回升,超过了阈值dirty_background_ration.
pdflush线程(实如今mm/pdflush.c中,回写机制的实现代码在文件mm/page-writeback.c和fs/fs-writeback.c中)周期地被唤醒而且把超过特定期限的脏页写回磁盘。系统管理员能够在/proc/sys/vm中设置回写相关的參数,也能够通过sysctl系统调用来设置它们。下表给出了能够设置的量:
Linux页快速缓存与回写机制分析的更多相关文章
- linux下数据同步、回写机制分析
一.前言在linux2.6.32之前,linux下数据同步是基于pdflush线程机制来实现的,在linux2.6.32以上的版本,内核彻底删掉了pdflush机制,改为了基于per-bdi线程来实现 ...
- linux块设备的IO调度算法和回写机制
************************************************************************************** 參考: <Linux ...
- CDN 的缓存与回源机制解析
CDN的缓存与回源机制解析 CDN (Content Delivery Network,即内容分发网络)指的是一组分布在各个地区的服务器.这些服务器存储着数据的副本,因此服务器可以根据哪些服务器与用户 ...
- Linux 3.2中回写机制的变革
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://alanwu.blog.51cto.com/3652632/1109952 wri ...
- rabbitMQ的简单实例——amqp协议带数据回写机制
rabbitMQ是一种高性能的消息队列,支持或者说它实现了AMQP协议(advanced message queue protocol高级消息队列协议). 下面简单讲一讲一个小例子.我们首先要部署好r ...
- 《Linux内核设计与实现》读书笔记(十六)- 页高速缓存和页回写
好久没有更新了... 主要内容: 缓存简介 页高速缓存 页回写 1. 缓存简介 在编程中,缓存是很常见也很有效的一种提高程序性能的机制. linux内核也不例外,为了提高I/O性能,也引入了缓存机制, ...
- Linux内核设计与实现 总结笔记(第十六章)页高速缓存和页回写
页高速缓存是Linux内核实现磁盘缓存.磁盘告诉缓存重要源自:第一,访问磁盘的速度要远远低于访问内存. 第二,数据一旦被访问,就很有可能在短期内再次被访问到.这种短时期内集中访问同一片数据的原理称作临 ...
- 聊聊高并发(三十四)Java内存模型那些事(二)理解CPU快速缓存的工作原理
在上一篇聊聊高并发(三十三)从一致性(Consistency)的角度理解Java内存模型 我们说了Java内存模型是一个语言级别的内存模型抽象.它屏蔽了底层硬件实现内存一致性需求的差异,提供了对上层的 ...
- linux页缓存
2017-04-25 本节就聊聊页缓存这个东西…… 一.概述 页缓存是一个相对独立的概念,其根本目的是为了加速对后端设备的IO效率,比如文件的读写.页缓存顾名思义是以页为单位的,目前我能想到的在两个地 ...
随机推荐
- ssh: connect to host github.com port 22: Connection refused
假设git例如,下面的问题时,远程推送: [fulinux@ubuntu learngit]$ git push -u origin master ssh: connect to host githu ...
- android 原生应用、Web应用、混合应用优缺点分析
近期开发几个项目,牵涉到android的几种开发模式.对于原生态开发.web 应用开发以及混合模式开发,本人觉得并非哪一种就是最好的,哪一种就是最差的,这个全然是依据项目的实际需求,选择一种合适的开发 ...
- java--照片和BYTE这些东西阵列
使用java,图像被变换成BYTE排列.和该阵列为图象,远程传输的图片进行 参考:http://blog.csdn.net/huang9012/article/details/18241539 代码例 ...
- error LNK2019: 解析的外部符号 __imp__DispatchMessageW@4,在函数的符号 _WinMain@16 据引述
错误: 1>WinMain.obj : error LNK2019: 解析的外部符号 __imp__DispatchMessageW@4,在函数的符号 _WinMain@16 据引述 1> ...
- mount命令使用具体解释(Linux)
linux是一个优秀的开放源代码的操作系统,能够执行在大到巨型小到掌上型各类计算机系统上,随着 linux系统的日渐成熟和稳定以及它开放源代码特有的优越性,linux在全世界得到了越来越广泛的应用. ...
- Java对存储过程的调用方法
本文将介绍Java怎样实现对存数过程的调用方法,作者用了几个样例进行了具体的说明,简单明了,很适合刚開始学习的人. 一.Java怎样实现对存储过程的调用: A:不带输出參数的 create proce ...
- Ubuntu14.04 工作区设置
记ubuntu您可以切换工作区,但我按住 Ctrl+Alt+方向键 交换器,有没有反应,在这样的使用切换啊. 原来Ubuntu14.04默认并没有开启,仅仅须要设置一下就OK了. 1.打开系统设置.外 ...
- java中System类简介(转)
上次面试中遇到的一个问题,问到System.out.println()中的out是不是内部类[不是内部类],当时就给问蒙了,直观感觉out应该是System类的一个属性,跟内部类有什么关系?而且之前整 ...
- 网站的SEO以及它和站长工具的之间秘密(转)
博客迁移没有注意 URL 地址的变化,导致百度和 google 这两只爬虫引擎短时间内找不到路.近段时间研究了下国内最大搜索引擎百度和国际最大搜索引擎google的站长工具,说下感受. 百度的站长工具 ...
- 微软发布Win10开发者指南视频
假设你是一个开发者,推荐你看看微软今天推出了一系列视频,标题是Win10开发人员指南,总体长度6时,多达22章内容,介绍很广泛.其实,即使你是编程新手或仅有兴趣,也值得一看. 开发人员Jerry Ni ...