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效率,比如文件的读写.页缓存顾名思义是以页为单位的,目前我能想到的在两个地 ...
随机推荐
- UVa 10491 - Cows and Cars
題目:有m+n個們,每個門後面有牛或者車:有n仅仅牛,m輛車,你選擇当中1個: 然後打開当中的k你沒有選中的門後是牛的,問你改變選時得到車的概率. 說明:數學題,概率.全概率公式就可以: 說明:第10 ...
- 使用Canvas和Paint自己绘制折线图
主要用于Canvas一个特别简单的小demo. 能够手动点击看每一个月份的数据.很easy.就是用paint在canvas上画出来的. 主要内容就是计算左边价格的位置,以下日期的位置,三根虚线的位置, ...
- mfc 链接时错误 文件函数重复定义
我在HeaderFile里新建了一个函数,然后在程序里调用,一直出现这个错误,说这个函数重复定义, 发现是VS自动加到External dependencies里面了.把HeaderFile里的函数文 ...
- target-action传值
Target-Action传值 实质就是:A页面要给B页面传值,A就提供接口出去,抓A到B内部来,A间接调用自己内部方法(相当于,A把自己内部需 要操作的方法, ...
- 为了圣像画系统V1.0Beta版(javascript)-GIS520社区
地图标绘系统V1.0測试版 (界面比較难看,之后再统一美化!) 演示地址:http://www.gis520.com/gis/plotdemo/index.html 查看可在菜单Map--> ...
- html 块状元素 行内元素 内联元素
块状(Block)类型的元素的width默认为100%,而行内(Inline)类型的元素则是根据自身的内容及子元素来决定宽度. 块元素(block element) address - 地址 bloc ...
- [Erlang危机](4.5)第四章练习
原创文章.转载请注明出处:server非业余研究http://blog.csdn.net/erlib 作者Sunface 联系邮箱:cto@188.com Exercises 练习 Review Qu ...
- Codeforces 487C. Prefix Product Sequence 逆+结构体
意甲冠军: 对于数字n, 他询问是否有1~n置换 这种布置能够在产品上模每个前缀n 有可能0~n-1 解析: 通过观察1肯定要在首位,n一定要在最后 除4意外的合数都没有解 其它质数构造 a[i]=i ...
- DLX 舞蹈链 精确覆盖 与 重复覆盖
精确覆盖问题:给定一个由0-1组成的矩阵,是否能找到一个行的集合,使得集合中每一列都恰好包含一个1 还有重复覆盖问题 dancing links 是 一种数据结构,用来优化搜索,不算是一种算法.(双向 ...
- 64位sql server 如何使用链接服务器连接Access
原文:64位sql server 如何使用链接服务器连接Access 测试环境 操作系统版本:Windows Server 2008 r2 64位 数据库版本:Sql Server 2005 64位 ...