图解Linux中的虚拟内存机制和内存映射

虚拟内存可以说是硬件异常、主存、外存和操作系统的完美交互,而且更妙的是,这个机制是完全自动运行的。如果我们理解一点点虚拟内存的原理,就可以理解经常出现的内存错误的原因,还可以理解什么是内存映射mmap。
深度详解Linux内核网络结构及分布
linux内核,进程调度器的实现,完全公平调度器 CFS

一、物理地址空间是什么

理解虚拟地址空间还得从物理地址空间开始说起。我们知道内存就像一个数组,每个存储单元被分配了一个地址,这个地址就是物理地址,所有物理地址构成的集合就是物理地址空间。物理地址也就是真实的地址,对应真实的那个内存条。

如果CPU使用物理地址向内存寻址的话,就是下面这样,这条指令中的地址就是数据真实存放的地址。
在这里插入图片描述

二、虚拟地址空间是什么

引入虚拟地址之后,对于每一个进程,操作系统提供一种假象,让每个进程感觉自己拥有一个巨大的连续的内存可以使用,这个虚拟的空间甚至还可以比内存的容量还大。这个“假象”就是虚拟地址空间。虚拟地址是面向每个进程的,只是一个“假象”罢了。

此时CPU使用虚拟地址向内存寻址,通过专用的内存管理单元(MMU)硬件把虚拟地址转换为真实的物理地址(地址翻译),操作系统负责把虚拟地址和物理地址的映射关系维护在页表之中。在这里插入图片描述
指令中的地址不是数据真实存放的地址

三、程序和进程

当我们写完代码,编译,链接并且生成可执行文件后,得到的这个东西就是一系列二进制代码的集合,我们管这东西叫做程序,存储在磁盘上。只有当我们执行这个文件后,程序才会被操作系统读入内存运行,但是注意系统并不会把程序全部读入内存,我们把正在运行的程序叫做进程。

从进程的视角来看,我的数据和代码被存放在一个连续的空间之中,每个区域分别有着不同的功能。典型的如存放代码的区域和存放数据的区域。在这里插入图片描述
然而我们知道,这只是个假象。代码和数据中的地址都是一个虚拟地址,还需要经过地址翻译才能得到真正的物理地址。

四、分页、页表和缺页异常

虚拟地址和物理地址的映射关系是以“页”为单位的。分页就是把整个虚拟内存和物理内存分割成大小固定的块,以一个页作为映射的最小单位。运行时,CPU请求一个虚拟地址,虚拟地址又被翻译为物理地址,从而确定数据在内存中的哪个位置。下面的页表中记录了这个进程虚拟内存每个页的映射关系。在这里插入图片描述
在这里插入图片描述
当CPU寻址的时候,这个映射会有三种可能。

未分配:虚拟地址所在的那一页并未被分配,代表没有数据和他们关联,这部分也不会占用内存。
未缓存:虚拟地址所在的那一页被分配了,但并不在内存中。
已缓存:虚拟地址所在的那一页就在内存中。
当访问一个未缓存的区域时,系统将产生缺页中断,然后进程被阻塞,等待操作系统将缺失的那一页从磁盘复制到内存。当复制完成后,CPU继续执行导致缺页中断的那条指令,此时就会正常执行了。这种仅在需要的时候将页面拷贝到内存的策略叫做按需调度页面。 可以想象当程序被装入内存的时候,开始时仅有有很小的一部分内容被放入内存。程序在运行中不断缺页,不断的把需要的部分拷贝进内存。
从上面的图中还可以看出,虚拟内存实际上就是磁盘的缓存。系统通过缺页中断的机制,小心的维护着每个进程的虚拟地址假象。

五、虚拟内存的应用

在Linux中,将一片虚拟内存和一个磁盘上的对象关联起来,并用磁盘上的对象初始化这片虚拟内存,这个机制就叫做内存映射。

1、化简资源的共享

当我们使用共享库的函数时时,例如printf(),没有必要为每个进程拷贝一本代码,这样太浪费内存了。我们只需要让每个进程的一块虚拟内存映射到相同的对象上就可以了。在这里插入图片描述
假如进程1和进程2想要共享同一个文件。其中文件A已经被映射到进程2(文件被缓存在内存中),进程2还是通过缺页中断载入的文件A。
在这里插入图片描述
此时进程1也打开文件A,由于文件的名字在系统中是唯一的,操作系统清楚文件A已经被缓存在内存之中。因此系统将进程1的虚拟内存映射到相同内存之中,完成文件的共享。在这里插入图片描述
2、零拷贝技术的一种实现

对于linux中最常用的I/O函数 read() 来说,文件先会被系统复制到内核空间的缓冲区,然后再复制到用户空间。读一个文件需要复制两次显然不是我们希望的,尤其是读大文件的时候, sad…

在这里插入图片描述
通过内存映射,我们可以绕过内核缓冲,直接将文件A映射到虚拟内存,这里一共就发生一次拷贝,nice~在这里插入图片描述

六、总结

虚拟内存无时无刻都在为我们工作,而且我们不需要任何干涉就能自动地工作。虚拟内存可以看成对磁盘的一个缓存,它通过缺页中断触发操作系统处理访问未缓存块的问题。虚拟内存可以应用在处理共享对象、减少I/O开销问题中。

总之虚拟内存十分的强大,以上介绍的仅仅是虚拟内存的一部分功能。如果文中出现错误欢迎指出,希望这篇文章对你有用。

Linux、C/C++技术交流群:【960994558】整理了一些个人觉得比较好的学习书籍、大厂面试题、和技术教学视频资料共享在里面,有需要的可以自行添加哦!~
在这里插入图片描述

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux内存整理是指通过对内存的碎片进行整理,以提高内存的利用率和性能。内存整理的实现可以分为以下几个步骤: 1. 判断内存是否存在碎片:在内存申请时,先检查申请的内存块是否只包含一个内存页,如果是,则说明不是由于碎片导致的内存不足。如果需要进行内存整理,则继续下一步。 2. 内存碎片整理:调用__alloc_pages_direct_compact()函数作为内存碎片整理的入口。在这一步,会调用try_to_compact_pages()函数来进行内存碎片整理。整理过程,会对内存页面按照可移动性进行分组,并进行页面迁移,以减少碎片的产生。 3. 继续申请内存块:在完成内存碎片整理后,继续调用get_page_from_freelist()函数来尝试申请内存块。这一步是为了确保内存整理后,可以继续满足进程对内存的需求。 总结起来,Linux内存整理包括判断内存是否存在碎片、进行内存碎片整理和继续申请内存块三个步骤,以提高内存的利用率和性能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [一篇图解Linux内存碎片整理](https://blog.csdn.net/youzhangjing_/article/details/128114719)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [Linux内存管理-内存规整](https://blog.csdn.net/m0_74282605/article/details/128921169)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值