对于精通 CURD 的业务同学,内存管理好像离我们很远,但这个知识点虽然冷门(估计很多人学完根本就没机会用上)但绝对是基础中的基础 。
这就像武侠小说中的内功修炼,学完之后看不到立竿见影的效果,但对你日后的开发工作是大有裨益的,因为你站的更高了 。
文中所有示例图都是我亲手画的,画图比码字还费时间,但大家看图理解比文字更直观,所以还是画了 。需要高清示例图片的同学,文末有获取方式自取 。
再功利点的说,面试的时候不经意间透露你懂这方面知识,并且能说出个一二三来,也许能让面试官对你更有兴趣,离升职加薪,走上人生巅峰又近了一步 。
前提约定:本文讨论技术内容前提,操作系统环境都是 x86架构的 32 位 linux系统 。
虚拟地址即使是现代操作系统中,内存依然是计算机中很宝贵的资源,看看你电脑几个T固态硬盘,再看看内存大小就知道了 。
为了充分利用和管理系统内存资源,Linux采用虚拟内存管理技术,利用虚拟内存技术让每个进程都有4GB 互不干涉的虚拟地址空间 。
进程初始化分配和操作的都是基于这个「虚拟地址」,只有当进程需要实际访问内存资源的时候才会建立虚拟地址和物理地址的映射,调入物理内存页 。
打个不是很恰当的比方,这个原理其实和现在的某某网盘一样 。假如你的网盘空间是1TB,真以为就一口气给了你这么大空间吗?那还是太年轻,都是在你往里面放东西的时候才给你分配空间,你放多少就分多少实际空间给你,但你和你朋友看起来就像大家都拥有1TB空间一样 。
虚拟地址的好处
- 避免用户直接访问物理内存地址,防止一些破坏性操作,保护操作系统
- 每个进程都被分配了4GB的虚拟内存,用户程序可使用比实际物理内存更大的地址空间
文章插图
用户空间内核空间
物理地址上面章节我们已经知道不管是用户空间还是内核空间,使用的地址都是虚拟地址,当需进程要实际访问内存的时候,会由内核的「请求分页机制」产生「缺页异常」调入物理内存页 。
把虚拟地址转换成内存的物理地址,这中间涉及利用MMU 内存管理单元(Memory Management Unit ) 对虚拟地址分段和分页(段页式)地址转换,关于分段和分页的具体流程,这里不再赘述,可以参考任何一本计算机组成原理教材描述 。
文章插图
段页式内存管理地址转换
Linux 内核会将物理内存分为3个管理区,分别是:
ZONE_DMADMA内存区域 。包含0MB~16MB之间的内存页框,可以由老式基于ISA的设备通过DMA使用,直接映射到内核的地址空间 。
ZONE_NORMAL普通内存区域 。包含16MB~896MB之间的内存页框,常规页框,直接映射到内核的地址空间 。
ZONE_HIGHMEM高端内存区域 。包含896MB以上的内存页框,不进行直接映射,可以通过永久映射和临时映射进行这部分内存页框的访问 。
文章插图
物理内存区划分
用户空间用户进程能访问的是「用户空间」,每个进程都有自己独立的用户空间,虚拟地址范围从从 0x00000000 至 0xBFFFFFFF 总容量3G。
用户进程通常只能访问用户空间的虚拟地址,只有在执行内陷操作或系统调用时才能访问内核空间 。
进程与内存进程(执行的程序)占用的用户空间按照「 访问属性一致的地址空间存放在一起 」的原则,划分成 5个不同的内存区域 。访问属性指的是“可读、可写、可执行等。
- 代码段代码段是用来存放可执行文件的操作指令,可执行程序在内存中的镜像 。代码段需要防止在运行时被非法修改,所以只准许读取操作,它是不可写的 。
- 数据段数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量 。
- BSS段BSS段包含了程序中未初始化的全局变量,在内存中 bss 段全部置零 。
- 堆 heap堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减 。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
推荐阅读
- 使用 UEFI 双启动 Windows 和 Linux
- 洗牌 面试遇到shuffle算法时,用这三种就够了
- 招聘|如何面试管理人员
- 一道头条面试题:如何实现 LRU 原理?
- 搞懂Android应用启动过程,再也不怕面试官了
- 让Linux系统操作更顺畅的几个便捷小工具
- 为什么给猫咪取的名字她听不懂 猫咪听得懂主人叫它名字吗
- Linux 上部署 Java 应用绕不开的命令,撒花啦
- Linux与网络设备 GRE配置经验总结
- Linux,软件安装依赖该怎么解决?