Linux内核缓存

news2024/12/24 9:28:02

【推荐阅读】

轻松学会linux下查看内存频率,内核函数,cpu频率

纯干货,linux内存管理——内存管理架构(建议收藏)

一篇长文叙述Linux内核虚拟地址空间的基本概括

页缓存和块缓存

内核为块设备提供了两种通用的缓存方案:

  • 页缓存:针对以页为单位的所有操作,例如内存映射技术,负责了块设备的大部分工作
  • 块缓存:以块为操作单位,存取的单位是设备的各个块,由于块长度取决于特定的文件系统,块缓存能处理不通长度的块

缓冲区曾经是块设备进行I/O操作的传统方法,目前只用于支持很小的读取操作,对于块传输的标准数据结构变为struct bio,这种方式可以合并统一请求中后续的块,加速处理,但是对于单个块的操作,缓冲区仍然是首选,例如经常按块读取元数据的系统。

很多场合下,页缓存和块缓存结合使用(一个缓存的页在写操作期间划分为不同的缓冲区,在更细的粒度识别出被修改的部分,在数据写回时,只需要回写被修改的部分,不需要整页传输)

页缓存的结构

管理和查找缓存的页

Linux采用基数树的数据结构管理页缓存中的页,如下图

树的根有一个简单的数据结构表示,包含了树的高度(所包含节点的最大层次数目)和一个指针,指向组成树的第一个节点的数据结构

树的节点具备两种搜索标记,二者用于指定给定页当前是否为脏(即页的内容和后备存储器的数据不同)或该页是否正在向底层块设备回写。标记会一直向上设置到根节点,如果某个层次 n+1 的节点设置了某个标记,那么 n 层次的父节点也会设置该标记。好处是内核可以判断在某个范围内是否有一页或多页设置了某个标记位

回写修改的数据

由于页缓存的存在,写操作不是直接对块设备进行,而是在内存中进行,修改的数据首先被收集起来,然后被传输到更低的内核层,在那里对写操作进一步优化,具体的优化过程详见 通用块设备层。这里从页缓存的视角来看,需要确定何时回写?

内核提供如下几种同步方案:

  • 内核守护进程pdflush周期性地同步,他们扫描缓存中的页,将超出一定时间没有与底层块设备同步的页写回
  • 如果缓存中修改的数据项数目在短期内内明显增加,内核会主动激活pdflush进程
  • 提供了系统调用给用户调用来写回未同步的数据,常用的有sync调用

为管理可以按整页处理和缓存各种不同对象,内核使用了**“地址空间”**抽象,将内存中的页和特定的块设备关联起来,每个地址空间都有一个“宿主”,所谓其数据来源,一般用inode表示

一般修改文件或者按页缓存的对象时,只会修改页的一部分,为节约时间,在写操作期间,会将缓存中的每一页划分为较小的单位,称为缓冲区,回写过程中以缓冲区为单位来操作

块缓存的结构

Linux早期版本只包含块缓存,用于加速文件操作和系统性能,底层块设备的块缓存存在内存的缓冲区中,可以加速读写(实现部分包含在fs/buffers.c中)

与内存页相比,块比较小而且长度可变(依赖于使用的块设备或者文件系统)

文件系统在处理元数据时,一般会使用块缓存;而裸数据的传输则按页进行

缓冲区的实现基于页缓存,Linux 2.6之前,缓冲区使用缓冲区头buffer head结构实现,在2.6之后,不再使用缓冲区头结构,而是使用bio结构

地址空间

地址空间建立了缓存数据与后备存储器之间的关联,实现了两个单元之间的转换机制

  • 内存中的页关联到每个地址空间,这些页表示缓存的内容
  • 后备存储器指定了填充地址空间中页的数据的来源,它是虚拟内存中区域到后备存储器(块设备)上对应位置的映射

数据结构

地址空间的基础是address_struct结构,定义如下:

主要成员:

  • 与地址空间管理的区域之间的关联,通过两个成员建立:host指向inode实例,指定了后备存储器;一个基数树的根page_tree列出了地址空间中所有的物理内存页
  • i_mmap是一棵树的根节点,包含了与该inode相关的所有普通内存映射,该树的作用在于,支持查找给定区间至少一页的所有内存区域

  • backing_dev_info是一个指针,指向另一个结构,包含了与地址空间相关的后备存储器的有关信息(后备存储器是指与地址空间相关的外部设备,用作地址空间中信息的来源,通常为块设备)
  • aps指针指向address_space_operation结构,其中包含了一组函数指针,指向用于处理地址空间的特定操作

地址空间与内核其他部分的关联如下图

页缓存的实现

分配页

page_cache_alloc用于为一个即将加入页缓存的新页分配数据结构,加上后缀_cold的函数是获取一个冷页

  • 首先page_cache_alloc将工作委托给alloc_pages,它从伙伴系统获得一个页帧
  • 接下来将新页添加到页缓存,这是在add_to_page_cache函数中实现,如下所示,radix_tree_insert将与页相关的page实例插入到地址空间的基数树,在页缓存中的索引和指向所属地址空间的指针保存在page的成员index和mapping中

内核还提供了另一个可选的参数add_to_page_cache_lru,他首先调用add_to_page_cache向地址空间的页缓存添加一页,然后使用lru_cache_add函数将该页加入到系统的LRU缓存

查找页

使用基数树判断给定页是否已经缓存,使用find_get_page实现该功能

其中radix_tree_lookup用于查找位于给定偏移量的页,在找到页之后,page_cache_get将页的引用计数加1

在页上等待

内核经常要在页上等待,直至其状态改变为预期值。例如,数据同步时需要确保对于某页的回写已经结束,此时内存页的内容和底层块设备是相同的。处于回写过程中的页会设置PG_writeback标志位

内核提供了wait_on_page_writeback函数,用于等待页的该标志位清除

wait_on_cahe_bit安装一个等待队列,进程可以在上面睡眠,直至PG_writeback标志位清除

另外也有等待页解锁的需求,使用函数wait_on_page_locked实现

对整页的操作

内核在块设备和内存之间传输数据时,相关的算法和数据结构都是以页为基本单位,而逐个缓冲区/块的传输会对性能产生负面影响。因此,在再重新设计块层的过程中,内核版本2.5引入BIO,来替代缓冲区,用于处理与块设备的数据传输。内核添加了4个函数,来支持读写一页或多页

其中,writeback_control用于精确控制回写操作的选项

这4个函数共同之处:都是构建一个BIO实例,用于对块层进行传输

以mpage_readpages为例,该函数需要nr_pages个page实例,以链表的形式通过参数pages传递进去,mapping是相关的地址空间,get_block用于查找匹配的块地址

该函数首先遍历所有的page实例,在循环的每一遍,首先将该页添加到地址空间相关的页缓存中,然后创建一个bio请求,从块层读取所需的数据

在do_mpage_readpage建立bio请求时,会包含此前各页的BIO数据,以构造一个合并的请求(将几页的读取合并到一个请求,而不是每页一个请求)

如果在循环结束时,do_mpage_readpage留下一个未处理的BIO请求,则提交该请求

页缓存预读

页缓存预读不是由页缓存独立完成的,还需要VFS和内存管理层的支持

预读在内核的几处都有涉及

  • do_generic_mapping_read:一个内核通用的读取例程
  • filemap_fault函数:缺页异常处理程序,负责为内存映射读取缺页

这两个函数在读取文件时会调用,其中通过系统调用read()正常读文件时,一般会调用do_generic_mapping_read函数,而内存映射时第一次读取文件内容时,会触发缺页中断调用filemap_fault函数

代码详细实现见 深入Linux内核架构 P458

下面以do_generic_mapping_read为例,来考察预读的具体过程

假定进程已经打开了一个文件,准备读取第一页,该页尚未读入页缓存,此时不会只读入一页,而是顺序读取多页,内核调用page_cache_sync_readahead读取一行中的8页(数字8是具体说明),第一页对于do_generic_mapping_read来说是立即可用的,而在实际需要之前就被读入页缓存的页,处于预读窗口中

之后进程继续读取接下来的页,在访问第6页(6是举例说明)时,内核检测到在该页设置了PG_Readahead标志。此时触发了一个异步操作,会在后台读取若干页(由于还有两页可用,所以不需要同步读取,但在后台进行的I/O操作需要确保进一步读取文件时,相关页已经读入页缓存)。page_cache_async_read函数负责发出异步读请求,它又会将窗口中的一页标记为PG_Readahead,在进程遇到该页时,又会触发异步读取,以此类推

最重要的问题在于预测预读窗口的最优长度。因此,内核会记录每个文件上一次的设置,使用file_ra_state关联到每个file实例

主要成员:

  • start:表示页缓存开始预读的位置
  • size:表示预读窗口的长度
  • async_size:表示剩余预读页的最小值,如果预读窗口中的页数等于这个值,则会触发异步预读
  • ra_pages:表示预读窗口的最大长度,内核读入的页数可以小于这个值,但是不能超过
  • prev_pos:表示前一次读取时,最后访问的位置

预读机制的实现涉及如下几个函数

其中,ondemand_readahead例程负责实现预读策略(即判断预读多少当前不需要的页),在确定预读窗口的长度之后,调用ra_submit,将技术性问题委托给__do_page_cache_readhead函数,其中页是在页缓存中分配的,而后由块层填充

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

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

相关文章

光华股份深交所上市:市值51亿 应收账款余额超5亿

雷递网 雷建平 12月8日浙江光华科技股份有限公司(简称:“光华股份”,证券代码:001333)今日在深交所主板上市。光华股份本次发行3200万股,发行价为27.76元,募资8.88亿元。光华股份开盘价为33.31元…

开源,是不道德的!

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,非公众号转载保留此声明。请删掉你的github开源代码,让CV工程师成为真正的工程师。不要做真正的代码分享,因为除了满足一下你的虚荣…

vue-cli中学习vue

vue部分知识 大部分学习内容及代码在gitee仓库 生命周期 基本介绍 生命周期描述beforeCreate组件实例被创建之初created组件实例已经完全创建beforeMount组件挂载之前mounted组件挂载到实例上去之后beforeUpdate组件数据发生变化,更新之前updated组件数据更新之后…

springboot知识点

基本介绍 微服务最早由Martin Fowler与James Lewis于2014年共同提出,微服务架构风格是一种使用一套小服务来开发单个应用的方式途径,每个服务运行在自己的进程中,并使用轻量级机制通信,通常是HTTP API,这些服务基于业…

TH8-小视频方案

TH8-小视频方案说明1、我的访客1.1、dubbo服务1.1.1、实体对象1.1.2、定义接口1.1.3、编写实现1.2、记录访客数据1.3、首页谁看过我1.3.1、VO对象1.3.2、MovementController1.3.3、MovementService2、小视频功能说明3、FastDFS2.1、FastDFS是什么?2.2、工作原理2.1.…

会员消费占比高达96%,孩子王究竟是怎么做到的?

👆点击关注公众号👆1.孩子王:依靠会员“稳江山”2021年上半年,增长黑盒独家发布了一篇关于孩子王的研究文章《万字拆解孩子王:充满矛盾的母婴零售之王》,彼时,孩子王尚在二度上市的前夕等待敲钟…

JAVA SCRIPT设计模式--行为型--设计模式之Template Method模板方法(22)

JAVA SCRIPT设计模式是本人根据GOF的设计模式写的博客记录。使用JAVA SCRIPT语言来实现主体功能,所以不可能像C,JAVA等面向对象语言一样严谨,大部分程序都附上了JAVA SCRIPT代码,代码只是实现了设计模式的主体功能,不代…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java课程教学过程f6oz5

对于即将毕业或者即将做课设的同学而言,由于经验的欠缺,面临的第一个难题就是选题,确定好题目之后便是开题报告,如果选题首先看自己学习那些技术,不同技术适合做不同的产品,比如自己会些简单的Java语言&…

BSV 上的 Graftroot

我们已经演示了如何使用无合约的合约在 BSV 上实现 Taproot。我们将展示了其后续提案 Graftroot 可以以类似的方式实施。 BTC 中的 Grabroot 与 Taproot 类似,有两种方式可以使用锁定在由多方创建的聚合公钥 P 中的资金: 合作案例:又名默认…

kubernetes 安装 Harbor 仓库

文章目录kubernetes 安装 Harbor 仓库1. 下载 Harbor2. 安装 docker3. 优化 docker 配置4. 下载 docker-compose5. 安装 Harbor:one: 上传 harbor 文件包:two: 解压:three: 修改配置文件:four: 执行安装脚本安装:five: 配置开机自启6. 登陆测试:one: 浏览器登陆:two: 命令行登陆…

为什么需要对相机标定?

以下内容来自系统教程如何搞定单目/鱼眼/双目/阵列 相机标定? 点击领取相机标定资料和代码 为什么需要对相机标定? 我们所处的世界是三维的,而相机拍摄的照片却是二维的,丢失了其中距离/深度的信息。从数学上可以简单理解为&…

Peppol网络对接流程

Peppol 代表泛欧在线公共采购,现在连接到 Peppol 的组织可以通过高度安全的国际网络交换商业文件。知行软件通过了 PEPPOL 的 AS2 及 AS4 测试,被 OpenPEPPOL AISBL 正式认证为 PEPPOL 接入点供应商。可以在Peppol查询到相关接入点信息,如下&…

TH9-搭建后台系统

TH9-搭建后台系统1、项目架构1.1 概述1.2 API网关1.2.1 搭建网关依赖引导类跨域问题配置类配置文件1.2.2 配置鉴权管理器1.3 Nacos配置中心1.3.1 添加依赖1.3.2 添加bootstrap.yml配置1.3.3 nacos添加配置2、后台系统2.1 概述2.2 环境前端搭建2.2.1 导入数据库2.2.2 导入静态页…

MYSQL-INNODB索引构成详解

作者:郑啟龙 摘要: 对于MYSQL的INNODB存储引擎的索引,大家是不陌生的,都能想到是 B树结构,可以加速SQL查询。但对于B树索引,它到底“长”得什么样子,它具体如何由一个个字节构成的&#xff0c…

插入排序

目录 插入排序 思路: 原理视频: 代码: 时间复杂度: 总结: 题目链接: 插入排序 题目描述: 插入排序基本思想是每一步将一个待排序的记录,插入到前面已经排好序的有序序列中去,直到插完所有元素为止。 输入N个整数,将它们从…

【设计模式】代理模式(Proxy Pattern)

代理模式属于结构型模式,当一个对象不处于相同内存空间时、创建开销大时、需要进行安全控制时或需要代理处理一些其他事物时可以使用代理模式。代理模式通过为另一个类提供一个替身类来控制对这个类的对象的访问。 文章目录代理模式的介绍代理的分类:优点…

了解CloudCompare软件

CloudCompare是一款基于GPL开源协议的3D点云处理软件, 打开一个点云; 按住鼠标拖动旋转会出现坐标轴; 如果选中 TLS/GBL, 会出来右边这个线框,可以一起旋转;查了一下,TLS/GBL似乎是地面激光雷达…

2022年国际工程行业研究报告

第一章 行业概况 国际工程是指一个工程项目从咨询、投资、招投标、承包(包括分包)、设备采购、培训到监理各个阶段的参与者来自不止一个国家,并且按照国际工程项目管理模式进行管理的工程。国际工程是一种综合性的国际经济合作方式,是国际技术贸易的一种…

了解Linux内核内存映射

【推荐阅读】 深入linux内核架构--进程&线程 路由选择协议——RIP协议 轻松学会linux下查看内存频率,内核函数,cpu频率 纯干货,linux内存管理——内存管理架构(建议收藏) 概述Linux内核驱动之GPIO子系统API接口 一. 内存映射原理 由于所…

shadow阴影属性

shadow阴影属性 盒子阴影CSS中新增了盒子阴影,我们可以使用box-shadow属性为盒子添加阴影 源代码 box-shadow: h-shadow v-shadow blur spread color inset; h-shadow 必需,水平阴影的位置,允许负值 v-shadow…