Linux内核设计与实现---页高速缓存和页回写

页高速缓存和页回写

  • 1 页高速缓存
  • 2 基树
  • 3 缓冲区高速缓存
  • 4 pdflush后台例程
    • 膝上型电脑模式
    • bdflush和kupdated
    • 避免拥塞的方法:使用多线程

页高速缓存(cache)是Linux内核实现的一种主要磁盘缓存,通过把磁盘中的数据缓存到物理内存中,把对磁盘的访问变成对物理内存的访问,主要用来减少对磁盘的I/O操作。它由RAM中的物理页组成,缓存中每一项对应着磁盘中的多个块,每当内核开始执行一个页I/O操作时,首先会检查需要的数据是否在高速缓存中,如果在,那么内核就直接使用高速缓存中的数据,从而避免访问磁盘。

高速缓存的价值在于两个方面:第一,访问磁盘的速度要远远低于访问内存的速度,因为,在内存访问数据比从磁盘访问速度更快。第二,数据一旦被访问,就很有可能在短期内再次被访问到。如果在第一次访问数据时缓存它,那就极有可能在短期内再次被高速缓存命中。

1 页高速缓存

页高速缓存缓存的是页。Linux页高速缓存的目标是缓存任何基于页的对象,这包含各种类型的文件和各种类型的内存映射。为了满足普遍性的要求,Linux页高速缓存使用address_space结构体描述页高速缓存中的页面。该结构体定义在文件linux/fs.h中。

struct address_space {struct inode		*host;		/* owner: inode, block_device */struct radix_tree_root	page_tree;	/* radix tree of all pages */spinlock_t		tree_lock;	/* and spinlock protecting it */unsigned int		i_mmap_writable;/* count VM_SHARED mappings */struct prio_tree_root	i_mmap;		/* tree of private and shared mappings */struct list_head	i_mmap_nonlinear;/*list VM_NONLINEAR mappings */spinlock_t		i_mmap_lock;	/* protect tree, count, list */atomic_t		truncate_count;	/* Cover race condition with truncate */unsigned long		nrpages;	/* number of total pages */pgoff_t			writeback_index;/* writeback starts here */struct address_space_operations *a_ops;	/* methods */unsigned long		flags;		/* error bits/gfp mask */struct backing_dev_info *backing_dev_info; /* device readahead, etc */spinlock_t		private_lock;	/* for use by the address_space */struct list_head	private_list;	/* ditto */struct address_space	*assoc_mapping;	/* ditto */
} __attribute__((aligned(sizeof(long))));

i_mmap字段是一个优先搜索树,它的搜索范围包含了在address_space中所有共享的与私有的映射页面。优先搜索树是一种将堆与radix树结合的快速检索树。address space空间大小由nrpages字段描述,表示共有多少页。
address_space结构往往会和某些内核对象关联,通常情况下会与一个索引节点关联,这时host域就会指向该索引节点,如果关联对象不是一个索引节点的话,host就被设置为NULL。
a_ops域指向地址空间对象中的操作函数表,操作函数表定义在linux/fs.h文件中,由address_space_operations结构体表示:

struct address_space_operations {int (*writepage)(struct page *page, struct writeback_control *wbc);int (*readpage)(struct file *, struct page *);int (*sync_page)(struct page *);/* Write back some dirty pages from this mapping. */int (*writepages)(struct address_space *, struct writeback_control *);/* Set a page dirty */int (*set_page_dirty)(struct page *page);int (*readpages)(struct file *filp, struct address_space *mapping,struct list_head *pages, unsigned nr_pages);/** ext3 requires that a successful prepare_write() call be followed* by a commit_write() call - they must be balanced*/int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);int (*commit_write)(struct file *, struct page *, unsigned, unsigned);/* Unfortunately this kludge is needed for FIBMAP. Don't use it */sector_t (*bmap)(struct address_space *, sector_t);int (*invalidatepage) (struct page *, unsigned long);int (*releasepage) (struct page *, int);ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,loff_t offset, unsigned long nr_segs);
};

2 基树

每个address_space对象都有唯一的基树(radix tree),它保存在page_tree结构体中。基树是一个二叉树,只要指定了文件偏移量,就可以在基树中迅速检索到希望的数据,页高速缓存的搜索函数find_get_page()要调用函数radix_tree_loopup(),该函数会在指定基树中搜索指定页面。

基树核心代码的通用形式可以在文件lib/radix-tree.c中找到,要想使用基树,需要包含头文件linux/radix_tree.h

3 缓冲区高速缓存

现在的Linux系统中已经不再有独立的缓冲区高速缓存了。但在2.2版本的内核中,存在两个独立的磁盘缓存:页高速缓存和缓冲区高速缓存。前者缓存页,后者缓存缓冲。两种缓存并不同一:一个磁盘块可以在两种缓存中同时存在,因此需要对缓存中的同一拷贝进行很麻烦的同步操作。
在2.4版本的内核开始,统一了这两种缓存,现在Linux只有唯一的页高速缓存。

4 pdflush后台例程

由于页高速缓存的缓存作用,写操作实际上会被延迟,当页高速缓存中的数据比磁盘存储的数据更新时,这时候页高速缓存中的数据被称为脏数据,脏数据所在的页被称为脏页,这些脏页最终必须被写回磁盘。在以下两种情况发送时,脏页被写回磁盘:

  • 当空闲内存低于一个特定的阈值时,内核必须将脏页写回磁盘,以便释放内存。
  • 当脏页在内存中驻留时间超过一个特定的阈值时,内核必须将超时的脏页写回磁盘,以确保脏页不会无限期地驻留在内存中。

上面两种工作的目的完全不同。在老内核中,这是由两个独立的内核线程分别完成(bdflush和kupdated两个线程)的,但是在2.6内核中,由一群内核线程,pdflush后台回写线程同一执行两种工作。首先,pdflush线程在系统中的空闲内存低于一个特定的阈值时,将脏页刷新回磁盘。此目的是在可用物理内存过低时,释放脏页以重新获得内存。特定的内存阈值可以通过dirty_backgriud_radio sysctl系统调用设置。当空闲内存比阈值dirty_background_ratio低时,内核便会调用wakeup_bdflush()唤醒一个pdflush线程。随后pdflush线程进一步调用函数background_writeout()开始将脏页回写磁盘。函数background_writeout()需要一个长整型参数,该参数指定试图写回的页面数目。函数background_writeout会连续写出数据,直到满足以下两个条件:

  • 已经有指定的最小数目的页被写出到磁盘
  • 空闲内存数已经回升,超过了阈值dirty_background_ratio

上述条件确保了pdflush操作可以减轻系统中内存不足的压力,回写操作不会在达到这两个条件前停止,除非pdflush写回了所有的脏页,没有剩下的脏页可以写回了。

为了满足第二个目标,pdflush后台例程会被周期性唤醒,将那些在内存驻留时间过长的脏页写出,确保内存中不会有长期存在的脏页。在系统启动时,内核初始化一个定时器,让它周期地唤醒pdflush线程,随后使其运行函数wb_kupdate()。该函数将把所有驻留时间超过百分之drity_expire_centisece秒的脏页写回。然后定时器将再次被初始化为百分之drity_expire_centisece秒后唤醒pdflush线程。

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

膝上型电脑模式

膝上型电脑模式是一种特殊的页回写策略,该策略主要目的是将磁盘转动的机械行为最小化,允许磁盘尽可能长时间停滞,以此延长电池供电时间。该模式可通过/proc/sys/vm/laptop_mode文件进行配置,通常,该文件内容为0,膝上电脑模式关闭,如果需要启动,则向配置文件写入1.

bdflush和kupdated

在2.6内核版本前,pdflush线程的工作是分别由bdflush和kupdated两个线程共同完成。当可用内存过低时,bdflush内核线程在后台执行脏页回写操作,与pdflush一样,它也有一组阈值参数,当系统中空闲内存消耗到特定内存阈值以下时,bdflush线程就被wakeup_bdflush函数唤醒。

bdflush和pdflush之间主要有两个区别。第一个是系统中只有一个bdflush线程,而pdflush线程的数目可以动态改变;第二个是bdflush线程基于缓冲,它将脏缓冲写回磁盘,pdflush基于页,它将整个脏页写回磁盘。

因为只有在内存过低和缓冲数量过大时,bdflush才刷新缓冲,所以kupdate线程被引入,以便周期性地写回脏页。

bdflush和kupdate内核线程现在完全被pdflush线程取代了。

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

bdflush仅仅只有一个线程,因此很有可能在页回写任务很重时,造成阻塞,这是因为单一的线程很可能堵塞在某个设备的已阻塞请求队列上,而其他设备的请求队列却没法得到处理。

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

通过一个简单的算法,pdflush线程的数目可以根据系统的运行时间进行调整,如果所有已存在的pdflush线程都已经持续工作1秒以上,内核就会创建一个新的pdflush线程。线程数量最多不能超过MAX_PDFLUSH_THREADS,默认值是8.如果一个pdflush线程睡眠超过1秒,内核就会终止该线程,线程的数量最少不得小于MIN_PDFLUSH_THREADS,默认值是2.pdflush线程数量取决于页回写的数量和阻塞情况,动态调整。

这种方式看起来很理想,但是如果每一个pdflush线程都挂起在同一个阻塞的队列上会怎么样?在这种情况下,多个pdflush线程的性能并不会比单个线程提高多少,反而会造成严重的内存浪费。为了克服这种负面影响,pdflush线程利用阻塞避免策略,它们会积极地试图写回那些不属于阻塞队列的页面。这样一来,pdflush通过分派回写工作,阻止多个线程在同一个忙设备纠缠。所以pdflush线程很忙,此时会有一个新的pdflush线程被创建,它们才是真正的繁忙。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379612.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EL技术

1&#xff0e;EL 表达式概述 EL&#xff08;Express Lanuage&#xff09;表达式可以嵌入在jsp页面内部&#xff0c;减少jsp脚本的编写&#xff0c;EL 出现的目的是要替代jsp页面中脚本的编写。 2&#xff0e;EL从域中取出数据(EL最重要的作用) jsp脚本&#xff1a;<%requ…

math.trunc_JavaScript中带有示例的Math.trunc()方法

math.truncJavaScript | Math.trunc()方法 (JavaScript | Math.trunc() Method) Math.trunc() is a function in math library of JavaScript that is used to extract the integer part of the given floating-point number. It removes the decimal and all the digits after…

.NET程序员的书单

zz from sjtu bbs: http://bbs.sjtu.edu.cn/bbscon?boardDotNET&fileM.1126188158.A 发信人: luckySeven(lucky为这位mm默哀), 信区: DotNET 标 题: .NET程序员的书单 发信站: 饮水思源 (2005年09月08日22:02:45 星期四), 转信 发信人: AtomAndBit (原子与比特), 信区: D…

SVN+AnkhSVN端配置

对于ankhSVN我想很多人不陌生&#xff0c;因为经常使用&#xff0c;但是我还是发现很多人并不怎么会配置&#xff0c;或者完全不知道其需要配置&#xff0c;如果不配置的话&#xff0c;当两个人同时需要修改某个文件的时候就容易中弹了。SVN默认是不支持“锁定-编辑-解锁”的&a…

Linux内核设计与实现---模块

模块1 构建模块放在内核源代码树中放在内核代码外2 安装模块3 产生模块依赖性4 载入模块5 管理配置选项6 模块参数7 导出符号表Linux内核是模块化组成的&#xff0c;它允许内核在运行时动态地向其中插入或从中删除代码。 与开发的内核核心子系统不同&#xff0c;模块开发更接近…

JSTL技术

1&#xff0e;JSTL概述 JSTL&#xff08;JSP Standard Tag Library)&#xff0c;JSP标准标签库&#xff0c;可以嵌入在jsp页面中使用标签的形式完成业务逻辑等功能。jstl出现的目的同el一样也是要代替jsp页面中的脚本代码。JSTL标准标准标签库有5个子库&#xff0c;但随着发展…

asinh函数_JavaScript中带有示例的Math.asinh()方法

asinh函数JavaScript | Math.asinh()方法 (JavaScript | Math.asinh() Method) Math.asinh() is a function in math library of JavaScript that is used to find the value of hyperbolic arc-sine of a number. Math.asinh()是JavaScript数学库中的函数&#xff0c;用于查找…

使用PHP创建一个REST API(Create a REST API with PHP)

译者前言&#xff1a; 首先这是一篇国外的英文文章&#xff0c;非常系统、详尽的介绍了如何使用PHP创建REST API&#xff0c;国内这方面的资料非常非常的有限&#xff0c;而且基本没有可操作性。这篇文章写的非常好&#xff0c;只要对PHP稍有了解的程序员&#xff0c;看完本文基…

old-

大数问题:求用一段C或C程序写求 f(x)100! 的完整程序大数问题&#xff0c; 我用数组作的&#xff0c;输出格式应该是是222,222,222 #include "stdafx.h" #include<stdio.h> #include<stdlib.h> int a[1000]{0}; in…

javaEE的开发模式

1&#xff0e;什么是模式 模式在开发过程中总结出的“套路”&#xff0c;总结出的一套约定俗成的设计模式 2&#xff0e;javaEE经历的模式 model1模式&#xff1a; 技术组成&#xff1a;jspjavaBean model1的弊端&#xff1a;随着业务复杂性 导致jsp页面比较混乱 model2模式…

Linux内核设计与实现---kobject sysfs

kobject sysfs1 kobject2 ktype3 kset4 subsystem5 别混淆了这些结构体6 管理和操作kobject7 引用计数kref8 sysfssysfs中添加和删除kobject向sysfs添加文件9 内核事件层2.6内核增加了一个引人注目的新特性—同一设备模型。设备模型提供了独立的机制专门表示设备&#xff0c;并…

开发Windows Mobile今日插件 -- 内存电量,桌面便笺,桌面记单词

本篇文章讲解的是开发 Windows Mobile 上的今日插件。关于是今日插件&#xff0c;在 PPC 或者 SP SDK 的帮助文档中有相关的章节介绍&#xff0c;在网络上也有一些帖子和资源讲解。在这里简要回顾一下。今日插件就是在windows mobile的桌面上显示的条目&#xff0c;例如系统提供…

c语言中将函数指针作为形参_在C中将有效指针作为NULL指针

c语言中将函数指针作为形参Prerequisite: An Example of Null pointer in C 先决条件&#xff1a; C中的空指针示例 Any pointer that contains a valid memory address can be made as a NULL pointer by assigning 0. 通过分配0&#xff0c;可以将包含有效内存地址的任何指…

[转]一个清华计算机博士生的退学申请

偶然间在网上看到这篇帖子&#xff0c;回想起自己的求学经历&#xff0c;思索良久。。。 本想找到原帖及作者&#xff0c;但是几经搜索&#xff0c;发现原帖出自科学网&#xff0c;已被删除。对此&#xff0c;我还能说啥&#xff1f;&#xff01; http://www.sciencenet.cn/m/u…

算法---递归

递归结题三部曲 何为递归&#xff1f;程序反复调用自身即是递归。 我自己在刚开始解决递归问题的时候&#xff0c;总是会去纠结这一层函数做了什么&#xff0c;它调用自身后的下一层函数又做了什么…然后就会觉得实现一个递归解法十分复杂&#xff0c;根本就无从下手。 相信…

给定条件找最小值c语言程序_根据给定条件最小化n的最小步骤

给定条件找最小值c语言程序Problem statement: 问题陈述&#xff1a; Given a number n, count minimum steps to minimize it to 1 performing the following operations: 给定数字n &#xff0c;执行以下操作&#xff0c;计算最少的步骤以将其最小化为1&#xff1a; Operat…

提高C#编程水平不可不读的50个要诀

提高C#编程水平的50个要点 1.总是用属性 (Property) 来代替可访问的数据成员 2.在 readonly 和 const 之间&#xff0c;优先使用 readonly 3.在 as 和 强制类型转换之间&#xff0c;优先使用 as 操作符 4.使用条件属性 (Conditional Attributes) 来代替条件编译语句 #if 5.总是…

那个年代的苏联歌曲

小时候&#xff0c;不时听父亲提起电影《这里的黎明静悄悄》&#xff0c;怎么也想不到如此美丽的名字为什么要和战争联系起来。后来在大学看了这部电影之后&#xff0c;开始认为这名字是合适的&#xff0c;因为电影讲的是女性——战场中的女性&#xff0c;各自都怀揣着爱情去保…

linux系统编程---进程总结

进程控制总结1 进程创建的三种方式forkvfrokclone2 进程终止进程正常退出returnexit_exit进程异常退出进程收到某个信号&#xff0c;而该信号使进程终止abort3 进程等待进程等待的方法waitwaitpid4 进程替换替换原理替换函数制作一个简单的shell1 进程创建的三种方式 参考文章…

银行账务转账系统(事务处理)

流程如下&#xff1a; 创建项目工程如下&#xff1a; transfer包下的代码如下&#xff1a; package beyond.transfer.dao;import java.sql.Connection; import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;import beyond.utils.DataSourceUtils;pu…