參考





《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页快速缓存与回写机制分析的更多相关文章

  1. linux下数据同步、回写机制分析

    一.前言在linux2.6.32之前,linux下数据同步是基于pdflush线程机制来实现的,在linux2.6.32以上的版本,内核彻底删掉了pdflush机制,改为了基于per-bdi线程来实现 ...

  2. linux块设备的IO调度算法和回写机制

    ************************************************************************************** 參考: <Linux ...

  3. CDN 的缓存与回源机制解析

    CDN的缓存与回源机制解析 CDN (Content Delivery Network,即内容分发网络)指的是一组分布在各个地区的服务器.这些服务器存储着数据的副本,因此服务器可以根据哪些服务器与用户 ...

  4. Linux 3.2中回写机制的变革

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://alanwu.blog.51cto.com/3652632/1109952 wri ...

  5. rabbitMQ的简单实例——amqp协议带数据回写机制

    rabbitMQ是一种高性能的消息队列,支持或者说它实现了AMQP协议(advanced message queue protocol高级消息队列协议). 下面简单讲一讲一个小例子.我们首先要部署好r ...

  6. 《Linux内核设计与实现》读书笔记(十六)- 页高速缓存和页回写

    好久没有更新了... 主要内容: 缓存简介 页高速缓存 页回写 1. 缓存简介 在编程中,缓存是很常见也很有效的一种提高程序性能的机制. linux内核也不例外,为了提高I/O性能,也引入了缓存机制, ...

  7. Linux内核设计与实现 总结笔记(第十六章)页高速缓存和页回写

    页高速缓存是Linux内核实现磁盘缓存.磁盘告诉缓存重要源自:第一,访问磁盘的速度要远远低于访问内存. 第二,数据一旦被访问,就很有可能在短期内再次被访问到.这种短时期内集中访问同一片数据的原理称作临 ...

  8. 聊聊高并发(三十四)Java内存模型那些事(二)理解CPU快速缓存的工作原理

    在上一篇聊聊高并发(三十三)从一致性(Consistency)的角度理解Java内存模型 我们说了Java内存模型是一个语言级别的内存模型抽象.它屏蔽了底层硬件实现内存一致性需求的差异,提供了对上层的 ...

  9. linux页缓存

    2017-04-25 本节就聊聊页缓存这个东西…… 一.概述 页缓存是一个相对独立的概念,其根本目的是为了加速对后端设备的IO效率,比如文件的读写.页缓存顾名思义是以页为单位的,目前我能想到的在两个地 ...

随机推荐

  1. java.lang.VerifyError

    public class VerifyErrorextends LinkageError Thrown when the "verifier" detects that a cla ...

  2. linux软与硬接线连接

    1.Linux链接概念 Linux链接分两种.一种被称为硬链接(Hard Link),还有一种被称为符号链接(Symbolic Link).默认情况下,ln命令产生硬链接. [硬连接] 硬连接指通过索 ...

  3. C#枚举数和迭代器

    大道至简,始终认为简洁是一门优秀的编程语言的一个必要条件.相对来说,C#是比较简洁的,也越来越简洁.在C#中,一个关键字或者语法糖在编译器层面为我们做了很多乏味的工作,可能实现的是一个设计模式,甚至是 ...

  4. net开源cms系统

    .net开源cms系统推荐 内容目录: 提起开源cms,大家第一想到的是php的cms,因为php开源的最早,也最为用户和站长们认可,随着各大cms系统的功能的不断完善和各式各样的开源cms的出现,. ...

  5. linux编curlDLL库so

    转载请注明出处:帘卷西风的专栏(http://blog.csdn.net/ljxfblog) curl库是一个非常强大的http开源库.c++里面可以非常方便的和httpserver交互. 近期项目開 ...

  6. java离request获取当前从访问完成url至

    request.getHeader("REFERER") 得到的完整路径到原始访问路径,其他参数 版权声明:本文博主原创文章.博客,未经同意不得转载.

  7. Dev GridView RowCellClick活动MouseDown事件

    GridView可编辑.在无声的思想左键点击"进入编辑". 将GridView的OptionsColumn.AllowEdit至false离开时触发RowCellClick. 但有 ...

  8. DDD领域驱动设计初探

    DDD领域驱动设计初探1 前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下D ...

  9. 【BZOJ3627】【JLOI2014】路径规划 分层图

    意甲冠军:要查找.有门户网站:http://www.lydsy.com/JudgeOnline/problem.php? id=3627 注意:点的等待时长示意图为: 非常清晰了吧?没事,不清晰就不清 ...

  10. directory not found for option

    directory not found for option '-LS60' 选择项目名称----->Targets----->Build Settings----->Search ...