Linux内存管理 —— 文件系统缓存和匿名页的交换

news2024/11/29 6:31:03

转自:https://www.cnblogs.com/wangshaowei/p/14089132.html

文件页

内存回收,也就是系统释放掉可以回收的内存,比如缓存和缓冲区,就属于可回收内存。它们在内存管理中,通常被叫做文件页(File-backed Page)。大部分文件页,都可以直接回收,以后有需要时,再从磁盘重新读取就可以了。

脏页

那些被应用程序修改过,并且暂时还没写入磁盘的数据(也就是脏页),就得先写入磁盘,然后才能进行内存释放。

这些脏页,一般可以通过两种方式写入磁盘。可以在应用程序中,通过系统调用 fsync ,把脏页同步到磁盘中;也可以交给系统,由内核线程 pdflush 负责这些脏页的刷新。

文件映射页

除了缓存和缓冲区,通过内存映射获取的文件映射页,也是一种常见的文件页。它也可以被释放掉,下次再访问的时候,从文件重新读取。

匿名页

应用程序动态分配的堆内存,也就是在内存管理中说到的匿名页(Anonymous Page),它们很可能还要再次被访问啊,不能直接回收,这些内存自然不能直接释放。但是,如果这些内存在分配后很少被访问,似乎也是一种资源浪费。

Linux Swap

Linux的 Swap 机制把这些不常访问的内存先写到磁盘中,然后释放这些内存,给其他更需要的进程使用。再次访问这些内存时,重新从磁盘读入内存就可以了。

详细解释

1. swap的含义

在Linux里swap有两个意思:
1. 动词:交换。内存和磁盘的颠簸行为。
2. 名词:硬盘的swap分区。

没有文件背景的页面,即匿名页(anonymous page),如堆,栈,数据段等,不是以文件形式存在,因此无法和磁盘文件交换,但可以通过硬盘上划分额外的swap交换分区或使用交换文件进行交换。即上面wap作为名词的意思。Swap分区可以将不活跃的页交换到硬盘中,缓解内存紧张。

注意,即使没有swap分区,也会存在swap行为,因为有文件背景的页面(file-backed page)也会有swap,即第1点的磁盘和内存之间的交换。

对于有文件背景的页面,程序去读文件时,可以通过read也可以通过mmap去读。当你通过任何一种方式从磁盘读文件时,内核都会给你申请一个page cache,来缓存硬盘上的内容。这样的话,读过一遍的数据,本进程或其他进程下次再读的时候就直接从page cache里去拿,就很快了,提升系统的整体性能。因此用户的read/write实际上是跟page cache的相互拷贝。
而用户的mmap则会将一段虚拟地址(3G)以下映射到page cache上,这样的话,用户就可以通过读写这段虚拟地址来修改文件内容,省去了内核和用户之间的拷贝。

所以文件对于用户程序来讲其实只是内存,page cache就是磁盘中文件的一个副本。可以通过 “echo 3 > /proc/sys/vm/drop_cache” 来清cache。清掉之后,进程第一次读文件就会变慢。

通过free命令可以看到当前page cache占用内存的大小,free命令中会打印buffers和cached(有的版本free命令将二者放到一起了)。通过文件系统来访问文件(挂载文件系统,通过文件名打开文件)产生的缓存就由cached记录,而直接操作裸盘(打开/dev/sda设备去读写)产生的缓存就由buffers记录。

root@jchen:~# free
             total         used         free       shared      buffers
Mem:        254316        68568       185748            0         6676
-/+ buffers:              61892       192424
Swap:            0            0            0

实际上文件系统本身再读写文件就是操作裸分区的方式,用户态也可以直接操作裸盘,像dd命令操作一个设备名也是直接访问裸分区。
那么,通过文件系统读写的时候,就会既有cached又有buffers。从图中可以看到,文件名等元数据和文件系统相关,是进cached,
实际的数据缓存还是在buffers。例如,read一个文件(如ext4文件系统)的时候,如果文件cache命中了,就不用走到ext4层,从vfs层就返回了。

当然,还可以在open的时候加上O_DIRECT标记,做直接IO,就连buffers都不进了,直接读写磁盘。
free命令的第二行打印即是将buffers/cache作为可用内存统计到used和free的列。

2. 页面回收(reclaim)

2.1 回收时机

有文件背景的数据实际上就是page cache,但page cache不能无限增加,不能说慢慢的所有文件都缓存到内存了。肯定要有一个机制,让不常用的文件数据从page cache刷出去。内核中有一个水位控制的机制,在系统内存不够用的时候,会触发页面回收。

对于没有文件背景的页面即匿名页,比如堆、栈、数据段,如果没有swap分区,不能与磁盘交换,就要常驻内存了。这里需要特殊说明一下数据段,数据段实际上在磁盘文件里,如果一个程序的全局变量的某一页的数据都还没有被修改过,就没必要产生匿名页,而一旦被修改了,就变成匿名页了,因为你不能回写磁盘啊,不能下次重新执行程序的时候变量初始值变了……。
但是常驻内存的话,就会吃内存,可以通过给硬盘搞一个swap分区或硬盘中创建一个交换文件(swapfile)让匿名页也能交换到磁盘上。可认为是为匿名页伪造的文件背景。swap分区或swap文件实际上最终是到达了增大内存的效果。当然,如果频繁交换的话,被交换出去的数据的访问就会慢一些,因为要有IO操作了。

无论是有文件背景的页还是匿名页,交换(这里指换出)的时机有两个:

  1. 内核通过kswapd内核线程慢慢回收,回收的时机由水位控制。
  2. 人为地主动地进行drop_cache。由于第1点要等到内存不足的时候才swap,可以通过这种方式主动发起回收。

内核中有个CONFIG_SWAP选项,可以控制匿名页的交换,如果关掉这个选项,就不能使用swap分区和交换文件了。但有文件背景的页本来就在磁盘里,因此仍可以交换,不受该选项影响。也可以在开了CONFIG_SWAP的情况下,通过swapoff命令,将匿名页的swap功能关掉(如果此时swap分区里有内容,则会先换入),相应的使用swapon命令重新打开匿名页的swap功能。

2.2 水位(watermark)控制

内核中有三个水位:

  • low:当剩余内存慢慢减少,触到这个水位时,就会触发kswapd线程的内存回收。
  • min:如果剩余内存减少到触及这个水位,可认为内存严重不足,当前进程就会被堵住,kernel会直接在这个进程的进程上下文里面做内存回收(direct reclaim)。
  • high: 进行内存回收时,内存慢慢增加,触到这个水位时,就停止回收。

由于每个ZONE是分别管理各自内存的,因此每个ZONE都有这三个水位。

swapness:
内存回收的过程就相应的会有page cache向磁盘或匿名页向swap分区回写的过程。回收的时候,是回收有文件背景的页还是匿名页呢,都会回收,但可通过/proc/sys/vm/swapness来控制让谁回收多一点点。这个值比较大时,就回收匿名页多一点点,比较小就反之。
所以swapness反映了是否积极地使用swap空间,而将swapness=0则意味着不再交换匿名页,除非当内存不足(free and file-backed pages < high watermark in a zone)的情况下才使用swap空间(这里的意思是,内存触到low之后就发起回收,直到内存回到high水位停止,但如果回收完file-backed页面都到不了high,就得开始回收匿名页了,这段时间内即使swapness=0也出现了回收匿名页的情况)。
另外需要注意,/proc/sys/vm/swapness是控制全局的swap特性的。cgroup的swapness优先级高些,如果一个cgroup的swapness关掉,全局的没关,那么这个cgroup里的进程的swap就是关掉的。也就是说,全局的swapness是控制不在cgroup里面的进程的swap特性的。

回收的过程是依据LRU,即最近最少使用的页会被回收,Linux内核一直在评估哪些是LRU的页面即最不活跃的页面。

root@none:~# cat /proc/meminfo
MemTotal:         254316 kB
MemFree:          185748 kB
Buffers:            6676 kB
Cached:            22716 kB
SwapCached:            0 kB
Active:            25472 kB   <----
Inactive:          23164 kB   <----
Active(anon):      19684 kB   <----
Inactive(anon):      456 kB   <----
Active(file):       5788 kB   <----
Inactive(file):    22708 kB   <----
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:         19272 kB
…… ……

这里cat /proc/meminfo看到的active和inactive的内存就是指lru算法里面去评估的一个页面的使用情况(有没有被访问过),inactive的页面中最inactive的页面最先被回收。如果inactive的页都回收了但内存仍然不够,也会从active的页中回收相对最不活跃的页面。

所以我们就知道,如果lowmem被使用殆尽,触及low或min水位,内核的普通kmalloc就申请不到内存了,就会触发cache/buffers的回收和匿名页swap,再不行就OOM了。

注意sync和swap的区别哦。sync是回写脏页,即page cache被修改后与磁盘原文件内容不同步的页,回写完后内存也不会回收,回收还是要等到kswapd或direct reclaim。进程打开并使用一个文件后调用close(),是不会回写脏页的,要显示地调用sync()/fsync()。

再说一下tmpfs,tmpfs是存放临时文件用的,还用于linux的posix和sysv共享内存,共享内存这种进程通信方式底层就是tmpfs。它其实是没有文件背景的,因此如果有swap,就交换到swap分区,没有就常驻内存。但是在统计内存的时候,是把tmpfs占的内存统计到page cache的,这里就有点绕。所以有时你在drop_cache后发现cache/buffers仍然很大,可能就是因为tmpfs的内存无法回收。
另外对于内核空间,内核的内存,如代码、数据、申请的内存,一般是不能被回收的。内核产生的文件cache、一些数据结构如dentry和inode等充当缓存的内存这些是可以回收的。

如何计算水位
/proc/sys/vm/min_free_kbytes 是一个用户可配置的值,默认值是根据每个lowmem zone的内存大小算出来的(不是随着内存大小线性增长的)。这个值就决定了min的值,然后根据min算出来low和high水位的值。结果就是high>low>min。
在/proc/zoneinfo中可以看到每个zone的水位情况。具体计算方法见init_per_zone_wmark_min(void)函数。

比较特殊的是highmem,highmem的水位不是根据min_free_kbytes计算,而是将其min设置为一个很小值,因为低水位是为了给紧急内存使用(如处理OOM也要使用内存),而紧急内存(__GFP_HIGH和PF_MEMALLOC)的分配不会在highmem上进行,因此不用预留太多。low和high仍然和其他zone一样由min计算得出,因为highmem仍有内存回收机制。

在kmalloc的时候加上PF_MEMALLOC标记就可以忽略内存管理的水位限制分配内存。当然,内核关键代码会这样用,你自己的代码就不要加这个标记了。

/proc/sys/vm/lowmem_reserve_ratio 可以对低端内存做进一步保护。我们知道越低端内存越珍贵,lowmem_reserve_ratio可以让kernel申请内存的时候不至于出现低端内存快用完了而高端内存还有可用的情况。它实际上是在watermark的基础上又预留出一段内存:在因为申请highmem得不到内存而转为向lowmem申请的情况下,lowmem的min就会变得严格,可能会让高端内存先尝试内存回收在分配内存。

3. 脏页的回写

上面提到了要注意区别sync和swap,这里也讲一下。sync是用来回写脏页的,脏页不能在内存中呆的太久,因为如果突然断电没有写到硬盘的脏数据就丢了,另一方面如果攒了很多一起写回也会明显占用CPU时间。

控制脏页何时写回:
下面这些变量是整个系统的,见kernel/sysctl.c中的定义:

static struct ctl_table vm_table[] = {
    ……
};

dirty_ratio: 一个写磁盘的进程所产生的脏页到达这个比例时,这个进程自己就会去回写脏页。
dirty_expire_centisecs: 脏页的到期时间,或理解为老化时间,单位是1/100s,内核中的flusher thread会检查驻留内存的时间超过dirty_expire_centisecs的脏页,超过的就回写。
dirty_writeback_centisecs: 内核的flusher thread周期性被唤醒(wakeup_flusher_threads())的时间间隔,每次被唤醒都会去检查是否有脏页老化了。如果将这个值置为0,则flusher线程就完全不会被唤醒了。
dirty_background_ratio: 如果脏页的数量超过这个比例时,flusher线程就会启动脏页回写。

因此可以看出,脏页回写的时机由时间(dirty_expire_centisecs/dirty_writeback_centisecs)和空间(dirty_ratio/dirty_background_ratio)两方面共同控制:

  1. 即使只有一个脏页,那如果它超时了,也会被写回。防止脏页在内存驻留太久。dirty_expire_centisecs这个值默认是3000,即30s,可以将其设置得短一些,这样掉电后丢失的数据会更少,但磁盘写操作也更密集。
  2. 不能有太多的脏页,否则会给磁盘IO造成很大压力,例如在内存不够做内存回收时,还要先回写脏页,也会明显耗时。

需要注意的是,在达到dirty_background_ratio后,flusher线程(名为“[flush-devname]”)开始回写,但由于写磁盘速度慢,如果此时应用进程还在不停地写磁盘,flusher线程回写没那么快,那么就会导致进程的脏页达到dirty_ratio,这时这个进程就会去回写脏页而导致write被堵住。也就是说dirty_background_ratio通常是比dirty_ratio小的。

脏页都是指有文件背景的页面,匿名页不会存在脏页。从/proc/meminfo的’Dirty’一行可以看到当前系统的脏页有多少,用sync命令可以刷掉。

补充:zRAM机制

不用swap分区,也可以用zRAM机制来缓解内存紧张: 从内存里拿出一段内存空间(compressed block),作为交换空间模拟硬盘的交换分区,用来交换匿名页,并且让kernel看到的物理内存大小不包括这段内存。而这段交换空间自带透明压缩功能,即交换到这块zRAM分区时,Linux会自动将这块匿名页压缩存放。系统访问这块页面的内容时,产生page fault后从交换分区去拿,这时Linux给你透明解压再交换出来。
使用zRAM的好处,就是访存比访问硬盘或flash的速度提高很多,且不用考虑寿命问题,并且由于这段内存是压缩后存储的,因此可以存更多的数据,虽然占用了一段内存,但实际可以存更多的数据,也达到了增加内存的效果。缺点就是压缩要占用CPU时间。

Android里面普遍使用了zRAM技术,由于zRAM牺牲了CPU时间,所以交换次数还是越少越好。像Android和windows,内存越大越好,因为发生交换的几率就小。这样两个进程相互切换(如微博和微信)时就会变得流畅,因为内存足够的话,后台进程无需被换进swap分区或被OOM杀掉。当然如果你只打打电话,就没必要大内存啦。

 

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

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

相关文章

B045-jQuery案例实战BootStrap

目录 一、BootStrap概述二、BootStrap使用1.起步1.下载或放入BootStrap文件到本地工程里2.在html代码中引入1个css文件和两个js文件3.复制模板代码到本地html文件里会自动应用成bootStrap图样中的效果0.实战案例-环境准备0.实战案例-基本模板 2.布局容器3.栅格系统4.常用组件表…

echarts里type为custom,自定义配置renderItem为柱状形状

echarts里type为custom&#xff0c;自定义配置renderItem为柱状形状 echarts里type为custom&#xff0c;自定义配置renderItem为柱状形状 echarts里type为custom&#xff0c;自定义配置renderItem为柱状形状 主要功能&#xff1a;文本超过柱状形状就隐藏否则就显示&#xff0c;…

风口上的AIGC,技术人才动不动就年薪百万?

2023年&#xff0c;职场人都在讨论什么&#xff1f; 自今年3月以来&#xff0c;随着ChatGPT应用持续走俏&#xff0c;AIGC领域抢人大战盛况空前。随之而来的便是“AI取代人类”“10亿打工人被革命”&#xff0c;AI的发展速度和步伐&#xff0c;超乎我们预期&#xff0c;也影响…

MySQL - 第0节 - MySQL在Centos 7环境安装

1.安装前说明 • 安装与卸载过程中&#xff0c;用户全部切换成为root&#xff0c;一旦安装&#xff0c;普通用户也能够使用。 • 初期练习&#xff0c;mysql不进行用户管理&#xff0c;全部使用root进行&#xff0c;后面学了用户管理&#xff0c;再考虑新建普通用户。 注&#…

互联网医院资质申请条件|互联网医院申请流程

互联网医院是医疗行业应用互联网技术的一种创新形态&#xff0c;它为患者提供便捷的医疗服务&#xff0c;改善了医疗资源的不足和分散情况。在中国&#xff0c;互联网医院的发展也越来越受到重视&#xff0c;互联网医院牌照的申请流程和需要的资料也成为了关注的焦点。 一、互…

SpringCloud 中各微服务使用 springcloud-config 获取配置文件时,配置信息无法实时刷新

文章目录 1、引入actuator监控2、修改 yml3、添加注解 RefreshScope4、业务操作5、发送通知&#xff0c;即可生效6、思考 使用 springcloud-config 作为 服务配置 管理时&#xff0c; 当对 git 上的配置进行修改后&#xff0c; 各微服务客户端 配置无法 实时刷新&#xff0c;需…

【Flutter】Flutter 切换语言 当前页面不刷新

文章目录 一、前言二、Flutter 多语言支持的基础知识三、如何在 Flutter 中设置语言四、如何在 Flutter 中切换语言五、代码示例六、完整代码七、总结 一、前言 大家好&#xff0c;今天我们要探讨的主题是如何在 Flutter 中切换语言&#xff0c;而不需要刷新当前页面。这是一个…

vue-element-admin - 最新完美解决项目是英文的问题,将英文变成中文的汉化处理详细教程(克隆完项目后不是中文的解决方法)

前言 网上的教程(并且都是好几年前的了)克隆运行后界面文字全都是英文的,如果您想要中文汉语版本请使用本文的方案。 本文 解决了克隆运行 vue-element-admin 项目后,默认是英文的问题!将语言设置为中文的详细教程! 官方文档说的很晦涩,您可以按照本教程步骤进行操作即…

log4j配置说明

目录&#xff1a; 1、 引入jar包使用 2、 使用 3、 配置文件 4、 配置 5、 说明 说明&#xff1a;2015年9月&#xff0c;Apache软件基金业宣布&#xff0c;Log4j不在维护&#xff0c;建议所有相关项目升级到Log4j2。 1.引入jar包使用 <dependency><groupI…

vscode 调试

目录 准备 GDB 调试方法 问题 准备 然后点击 文件-打开文件夹&#xff0c;找到创建的代码路径&#xff0c;确定后&#xff0c;在左侧的资源管理器可以看到代码文件。 第一次运行需要安装 c 的扩展&#xff0c;在扩展页面中&#xff0c;安装 C/C 编译注意一定要加上 -g 指令…

SpringBoot项目配置方式及优先级

说明&#xff1a;SpringBoot支持以下五种方式配置方式&#xff0c;例如将项目的Tomcat端口从8080&#xff0c;更改为9000&#xff0c;可以使用如下方式配置 【方式一】命令行参数 在启动窗口&#xff0c;鼠标右键&#xff0c;选择“Edit Configurations”&#xff0c;在弹出来…

开会糟透了?试试无声会议吧

引言 无声会议(Silent Meeting) 是一种已被成功实践但却鲜有公开资料的开会方法&#xff0c;本文会探寻下它的有趣历史&#xff0c;并介绍如何通过它的小小改变让会议变得不再糟糕。 文档是无声会议的核心基础&#xff0c;本文会引出 Coda 和 Mojidoc(妙记多) 这两款让你的会…

泛微数字化国有资产管理平台,以智能增效能

2021年国务院发布的《行政事业性国有资产管理条例》中要求应当建立健全行政事业性国有资产管理机制&#xff0c;加强对本级行政事业性国有资产的管理。 随着数字化转型的深入&#xff0c;越来越多的行政事业单位、国有企业运用数字化的理念、思路、方法&#xff0c;以智能增效…

AI工具助力创意与写作:19岁小伙每月赚3万,学会这些你也能轻松做到!

导语&#xff1a;在数字时代&#xff0c;人工智能为我们提供了许多创新的工具和资源&#xff0c;助力我们在各个领域实现创意和写作的目标。本文将介绍五款热门的AI工具&#xff1a;Copyai、Flikiai、Notion、TomeAl和Writesonic&#xff0c;它们分别在写作、视频配音、文本编辑…

Java调用Midjourney进行AI画图原生版抓包实现支持中文

用途介绍 Midjourney是一个目前优秀的AI画图工具&#xff0c;不挂梯无法直接访问 本代码主要用于搭建镜像站使用 适合人群 本代码不适合新手&#xff0c;建议使用过okhttp、且具有二开能力的同学使用~ 实现原理 通过调用发送信息接口发送请求&#xff0c;通过轮询房间消息…

【2023最全教程】Web自动化测试怎么做?Web自动化测试的详细流程和步骤

一、什么是web自动化测试 自动化&#xff08;Automation&#xff09;是指机器设备、系统或过程&#xff08;生产、管理过程&#xff09;在没有人或较少人的直接参与下&#xff0c;按照人的要求&#xff0c;经过自动检测、信息处理、分析判断、操纵控制&#xff0c;实现预期的目…

Spring @Autowired注入太坤肋了 我们自己写一个

1、 背景 众所周知该注解是Spring中用于依赖注入的注解&#xff0c;但是该注解只是简单根据类型的注入&#xff0c; 并且如果该类型存在多个实现类的情况下无法抉择具体哪一个实现类就会抛出错误&#xff0c;除非搭配Qualifier注解然后使用硬编码的方式去指定Bean的名字去抉择…

8年测试岗总结,软件测试常见面试题+答案,轻松避坑...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、介绍一下测试流…

JAVA开发与运维(怎么通过docker部署微服务jar包)

目标&#xff1a; 通过docker的方式部署微服务。 一、背景&#xff1a; 我们通过java开发的微服务可以打成jar包&#xff0c;我们可以直接通过裸机部署&#xff0c;也可以通过docker来部署&#xff0c;本文介绍通过docker来部署微服务。 二、首先我们介绍一下docker的发展过程…

Redis高级篇—分布式缓存 持久化 主从 哨兵 分片

分布式缓存 -- 基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题&#xff1a; 0.学习目标 1.Redis持久化 Redis有两种持久化方案&#xff1a; RDB持久化 AOF持久化 1.1.RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&a…