页高速缓存是Linux内核实现磁盘缓存。磁盘告诉缓存重要源自:第一,访问磁盘的速度要远远低于访问内存。

第二,数据一旦被访问,就很有可能在短期内再次被访问到。这种短时期内集中访问同一片数据的原理称作临时局部原理。

一、缓存手段

1.1 写缓存

通常来讲缓存一般实现成三种策略:

①不缓存

②写操作将自动更新内存缓存

③Linux采用的回写。程序写进缓存

1.2 缓存回收

1.最近少用原则:简称LRU,LRU回收需要跟踪每个页面的访问踪迹,以便能回收最老时间戳的页面。

2.双链策略:Linux实现的是一个修改过的LRU,也称为双链策略。Linux维护两个链表:活跃链表和非活跃链表。处于活跃链表是不会被换出的,而在非活跃链表上则是可以被换出的。这种链表方式也称作LUR/2,更普遍的是n个链表,故称LRU/n

二、Linux页高速缓存

高速缓存缓存的是内存页面,缓冲中的也来自对正规文件、块设备文件和内存映射文件的读写。

2.1 address_space对象

为了维持页高速缓存的普遍性,Linux页高速缓存使用了一个新对象管理缓存项和页I/O操作。address_space有点不确切,应该叫他page_cache_entity或者physical_page_of_a_file比较好。该结构定义在文件<linux/fs.h>中。

  1. struct address_space {
  2. struct inode *host; /* owner: inode, block_device 拥有节点*/
  3. struct radix_tree_root page_tree; /* radix tree of all pages 包含全部页面的radix树*/
  4. spinlock_t tree_lock; /* and lock protecting it 保护page_tree自旋锁*/
  5. atomic_t i_mmap_writable;/* count VM_SHARED mappings VM_SHARED计数*/
  6. struct rb_root i_mmap; /* tree of private and shared mappings 私有映射链表*/
  7. struct rw_semaphore i_mmap_rwsem; /* protect tree, count, list */
  8. /* Protected by tree_lock together with the radix tree */
  9. unsigned long nrpages; /* number of total pages 页总数*/
  10. unsigned long nrshadows; /* number of shadow entries */
  11. pgoff_t writeback_index;/* writeback starts here 回写的起始偏移*/
  12. const struct address_space_operations *a_ops; /* methods 操作表*/
  13. unsigned long flags; /* error bits/gfp mask gfp_mask掩码与错误标识*/
  14. spinlock_t private_lock; /* for use by the address_space 私有address_space锁*/
  15. struct list_head private_list; /* ditto 私有address_space链表*/
  16. void *private_data; /* ditto */
  17. } __attribute__((aligned(sizeof(long))))

struct address_space

2.2 address_space操作

address_space_operations定义在文件<linux/fs.h>中

  1. struct address_space_operations {
  2. int (*writepage)(struct page *page, struct writeback_control *wbc);
  3. int (*readpage)(struct file *, struct page *);
  4.  
  5. /* Write back some dirty pages from this mapping. */
  6. int (*writepages)(struct address_space *, struct writeback_control *);
  7.  
  8. /* Set a page dirty. Return true if this dirtied it */
  9. int (*set_page_dirty)(struct page *page);
  10.  
  11. int (*readpages)(struct file *filp, struct address_space *mapping,
  12. struct list_head *pages, unsigned nr_pages);
  13.  
  14. int (*write_begin)(struct file *, struct address_space *mapping,
  15. loff_t pos, unsigned len, unsigned flags,
  16. struct page **pagep, void **fsdata);
  17. int (*write_end)(struct file *, struct address_space *mapping,
  18. loff_t pos, unsigned len, unsigned copied,
  19. struct page *page, void *fsdata);
  20.  
  21. /* Unfortunately this kludge is needed for FIBMAP. Don't use it */
  22. sector_t (*bmap)(struct address_space *, sector_t);
  23. void (*invalidatepage) (struct page *, unsigned int, unsigned int);
  24. int (*releasepage) (struct page *, gfp_t);
  25. void (*freepage)(struct page *);
  26. ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter, loff_t offset);
  27. /*
  28. * migrate the contents of a page to the specified target. If
  29. * migrate_mode is MIGRATE_ASYNC, it must not block.
  30. */
  31. int (*migratepage) (struct address_space *,
  32. struct page *, struct page *, enum migrate_mode);
  33. int (*launder_page) (struct page *);
  34. int (*is_partially_uptodate) (struct page *, unsigned long,
  35. unsigned long);
  36. void (*is_dirty_writeback) (struct page *, bool *, bool *);
  37. int (*error_remove_page)(struct address_space *, struct page *);
  38.  
  39. /* swapfile support */
  40. int (*swap_activate)(struct swap_info_struct *sis, struct file *file,
  41. sector_t *span);
  42. void (*swap_deactivate)(struct file *file);
  43. };

struct address_space_operations

里面readpage()和writepage()两个方法最为重要,其中读操作会包括如下动作:

Linux内核试图在页高速缓存中找到需要的数据,find_get_page()方法负责完成这个动作检查。一个address_space对象和一个偏移量会传给find_get_page()方法,用于在页高速缓存中搜索需要的数据。

  1. page = find_get_page(mapping, index);

mapping:指定的地址空间,index:文件中的指定位置,以页面为单位。

如果搜索页没在高速缓存中返回NULL,内核将分配一个新页面。

  1. struct page *page;
  2. int error;
  3.  
  4. /* 分配页... */
  5. page = page_cache_alloc_cold(mapping);
  6. if(!page)
  7. /* 内存分配出错 */
  8.  
  9. /* ...然后将其加入到页面调整缓存 */
  10. error = add_to_page_cache_lru(page, mapping, index, GFP_KERNEL);
  11. if(error)
  12. /* 页面被加入到页面高速缓存时,出错 */
  13.  
  14. /* 需要的数据从磁盘被读入,再被加入页高速缓存,然后返回给用户 */
  15. error = mapping->a_ops->readpage(file, page);
  16. SetPageDirty(page); /* 写操作和读操作有少许不同 */

分配页,加入到页缓存

内核会在晚些时候嗲用writepage()写出,在文件mm/filemap.c中,比较复杂。主要步骤有:

  1. page = __grab_cache_page(mapping, index, &cached_page, &lru_pvec);
  2. status = a_ops->prepare_write(file, page, offset, offset+bytes);
  3. page_fault = filemap_copy_from_user(page, offset, buf, bytes);
  4. status = a_ops->commit_write(file, page, offset, offset+bytes);

写出

2.3 基数

每个address_space都有唯一的基树(radix tree),保存在page_tree。基树是二叉树,指定了文件偏移量,就可以在基树中迅速检索到希望的页。

find_get_page()要调用函数radix_tree_lookup()。基树核心代码的通用形式可以在文件lib/radix-tree.c中找到,想要使用基树,需要头文件<linux/radix_tree.h>

2.4 以前的页散列表

全局散列表主要的存在四个问题:

  • 由于使用单个的全局锁保护散列表,所以即使在中等规模的机器中,锁的征用情况也会相当严重,造成性能受损。
  • 由于散列表需要包含所有也高速缓存中的页,搜索需要的只是当前文件相关的那些页,所以散列表包含的页面相比搜索需要的页面要大的多。
  • 如果散列搜索失败,执行速度比希望的要慢的多,这是因为检索必须遍历指定散列键值对应的整个链表
  • 散列表比其他方法会消耗更多的内存

2.6版本内核引入基于基树的页高速缓存来解决这些问题。

三、缓冲区高速缓存

独立的磁盘块通过块I/O缓冲也要被存入页高速缓存。 因为它缓存磁盘块和减少块I/O操作,这个缓存童话参观称为缓冲区高速缓存,虽然没有独立缓存,而是作为页高速缓存的一部分。

块I/O操作一次操作一个单独的磁盘块

四、flusher线程

当页高速缓存中的数据比后台存储的数据更新时,该数据就称作脏数据。在内存中积累的脏页最终必须被写回磁盘。3中情况脏页写回磁盘:

  • 当空闲内存低于一个特定的阈值时,内核必须将脏页写回磁盘以便释放内存。(只有干净的内存才可以被回收)
  • 当脏页在内存中驻留时间超过一个特定的阈值时,内核必须将超时的脏页写回磁盘。
  • 当用户进程调用sync()和fsync()系统调用时,内核会按要求执行回写动作。

flusher线程实现代码在文件mm/page-writeback.c和mm/backing-dev.c中,回写机制的实现代码在文件fs/fs-writeback.c中。

4.1 膝上型计算机模式

4.2 历史上bdflush、kupdated和pdflush

4.3 避免堵塞的方法:使用多线程

内核通过使用多个flusher线程来解决上述问题。每个线程可以互相独立地将脏页刷新回磁盘,而且不同的flusher线程处理不同的设备队列。

Linux内核设计与实现 总结笔记(第十六章)页高速缓存和页回写的更多相关文章

  1. Linux内核设计与实现 总结笔记(第六章)内核数据结构

    内核数据结构 Linux内核实现了这些通用数据结构,而且提倡大家在开发时重用. 内核开发者应该尽可能地使用这些数据结构,而不要自作主张的山寨方法. 通用的数据结构有以下几种:链表.队列.映射和二叉树 ...

  2. Linux内核设计与实现 总结笔记(第十三章)虚拟文件系统

    一.通用文件系统接口 Linux通过虚拟文件系统,使得用户可以直接使用open().read().write()访问文件系统,这种协作性和泛型存取成为可能. 不管文件系统是什么,也不管文件系统位于何种 ...

  3. Linux内核设计与实现 总结笔记(第三章)进程

    进程管理 进程:处于执行期的程序. 线程:在进程中活动的对象 虚拟机制 虚拟处理器:多个进程分享一个处理器 虚拟内存:多个线程共享虚拟内存 一.进程描述符和任务结构 进程存放在双向循环链表中(队列), ...

  4. Linux内核设计与实现 总结笔记(第十一章)定时器和时间管理

    时间管理在内核中占用非常重要的地位,内核中有大量的函数都需要基于时间驱动的,内核对相对时间和绝对时间都非常需要. 一.内核中的时间概念 内核必须在硬件的帮助下才能计算和管理时间,系统定时器以某种频率自 ...

  5. Linux内核设计与实现 总结笔记(第五章)系统调用

    系统调用 内核提供了用户进程和内核交互的接口,使得应用程序可以受限制的访问硬件设备. 提供这些接口主要是为了保证系统稳定可靠,避免应用程序恣意妄行. 一.内核通信 系统调用在用户空间进程和硬件设备之间 ...

  6. Linux内核设计与实现 总结笔记(第七章)中断和中断处理

    中断和中断处理 处理器的速度跟外围硬件设备的速度往往不再一个数量级上,因此,如果内核采取让处理器向硬件发出一个请求. 然后专门等待回应的办法,如果专门等待回应,明显太慢.所以等待期间可以处理其他事务, ...

  7. Linux内核设计与实现 总结笔记(第四章)进程调度

    进程调度 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间. 调度程序没有太复杂的原理,最大限度地利用处理器时间的原则是,只要有可以执行的进程,那么就总会有进程正在执行. 一.多任务 多任 ...

  8. 《Linux内核设计与实现》课本第十八章自学笔记——20135203齐岳

    <Linux内核设计与实现>课本第十八章自学笔记 By20135203齐岳 通过打印来调试 printk()是内核提供的格式化打印函数,除了和C库提供的printf()函数功能相同外还有一 ...

  9. Linux内核设计与实现 读书笔记 转

    Linux内核设计与实现  读书笔记: http://www.cnblogs.com/wang_yb/tag/linux-kernel/ <深入理解LINUX内存管理> http://bl ...

  10. 《HTTP 权威指南》笔记:第十六章&第十七章 国际化、内容协商与转码

    <HTTP 权威指南>笔记:第十六章 国际化 客户端通过在请求报文中的 Accept-Language 首部和 Accept-Charset 首部来告知服务器:“我理解这些语言.”服务器通 ...

随机推荐

  1. c++中byte数组与字符串的转化

    我们不讨论与字符集有关的内容,只讨论在字节流传递过程中的问题. 我们在做一系统操作时会需要使用到数据流,比如接收网络数据,文件数据,图片数据,原始数据大多是以byte数组的形式提供,与其它语言(c#, ...

  2. JavaScript中JSON的序列化和解析

    1.序列化:JSON对象----->JSON格式字符串 ①方法: JSON.stringify() ②示例程序: var jsonString = JSON.stringify(obj); al ...

  3. Linux操作系统优化

    figure:first-child { margin-top: -20px; } #write ol, #write ul { position: relative; } img { max-wid ...

  4. Python解释器判断整数相加溢出

    溢出,则和的最高位(即符号位)与两个加数都不相同,例如 1)非负数+非负数=负数 2)负数+负数=非负数 那么,假设x为a与b的和,((a^b)>=0 && (x^a)<0 ...

  5. Java数据结构之算法时间度

    1.度量一个程序(算法)执行时间的两种方法 1)事后统计的方法 这种方法可行, 但是有两个问题:一是要想对设计的算法的运行性能进行评测,需要实际运行该程序:二是所得时间的统计量依赖于计算机的硬件.软件 ...

  6. SpringBoot整合mybatis碰到的问题

    整合mybatis 1.  导包:在原有的web项目的基础上加上 <!--JDBC连接-->     <dependency>         <groupId>m ...

  7. 解决跳转出现 No input file specified.

    项目根目录中.htaccess文件修改为: <IfModule mod_rewrite.c>  Options +FollowSymlinks  RewriteEngine On Rewr ...

  8. iconv编码转换报错问题

    今天,再由ISO-8859编码格式转化为UTF-8格式过程中,出现报错:iconv: 未知 10304 处的非法输入序列. 问题分析:ISO-8859是英文格式的编码方式,不支持中文,为了解决中文支持 ...

  9. python中,a=10.0 b=10.0 a is b 为什么输出是false

    >>>a=10.0>>>b=10.0>>>a is bFalse为什么当a=10,b=10时,a is b输出的是True呢? >>& ...

  10. 说说 HeapSort 堆排序思想,以及个人优化方案。(老物)

    听说你要排上亿个数据之 HeapSort ? 前言 : 来来来,今天我们来说说一个用来排大量数据所用的基础比较排序吧~ 注:阅读本文学习新技能的前置要求为:了解什么是二叉树及其数组性质,如果未达到要求 ...