目录
一、前言
二、冯·诺伊曼结构
1、早期计算机结构
2、理论提出
3、五大部件
三、存储器
1、存储器的种类
2、摩尔定律
3、存储层次
4、关于内存分层的思考
四、内存管理的需求和实现
1、需求确认
2、实现方式
五、程序重定位
1、地址空间
2、链接阶段
3、载入阶段
4、地址空间的读取过程
一、前言
学妹刚上大学,问我计算机内存知识需要了解么?我当场就是傻瓜警告,于是就有了这篇文章。
为什么要去了解内存知识?因为它是计算机操作系统中的核心功能之一,各高级语言在进行内存的使用和管理上,无一不依托于此底层实现,比如我们熟悉的Java内存模型。
最近几篇文章学习操作系统的内存管理后,喜欢底层的同学可以去学习 CPU 结构、机器语言指令和程序执行相关的知识,而看重实用性的同学后续学习多进程多线程和数据一致性时,可以有更深刻的理解。
二、冯·诺伊曼结构
1、早期计算机结构
在冯·诺依曼结构提出之前的计算机,是一种计算机只能完成一种功能,编辑好的程序是直接集成在计算机电路中,例如一个计算器仅有固定的数学计算程序,它不能拿来当作文字处理软件,更不能拿来玩游戏。若想要改变此机器的程序,你必须更改线路、更改结构甚至重新设计此计算机。
简单来说,早期的计算机是来执行一个事先集成在电路板上的某一特定的程序,一旦需要修改程序功能,就要重新组装电路板,所以早期的计算机程序是硬件化的。
2、理论提出
1945年,冯·诺依曼由于在曼哈顿工程中需要大量的运算,从而使用了当时最先进的两台计算机 Mark I 和 ENIAC,在使用 Mark I 和 ENIAC 的过程中,他意识到了存储程序的重要性,从而提出了“存储程序”的计算机设计理念,即将计算机指令进行编码后存储在计算机的存储器中,需要的时候可以顺序地执行程序代码,从而控制计算机运行,这就是冯.诺依曼计算机体系的开端。
这是对计算机发展有深刻意义的重要理论,从此我们开始将程序和数据一样看待,程序也在存储器中读取,这样计算机就可以不单单只能运行事先编辑集成在电路板上的程序了,程序由此脱离硬件变为可编程的了,而后诞生程序员这个职业。
关于冯·诺依曼这位大神,值得单独开一篇文章来聊聊。
3、五大部件
冯诺依曼计算机体系结构如下:
冯·诺依曼结构用极高的抽象描述了计算器的五大部件,以及程序执行时数据和指令的流转过程。现在快速的将现代计算机的设计概念结构了解后,我们把目光聚焦在本文的主角——存储器上。
三、存储器
1、存储器的种类
我们编写的程序、下载的电影,自然需要有个地方存放这些数据,存储器现在主要有易失性存储器和非易失性存储器两种。存取速度上来看,前者要快很多。数据持久化上来看,当电源供应中断后,易失性存储器所存储的数据便会消失,而非易失性存储器所存储的数据并不会消失,重新供电后,就能够读取存储器中的数据。
易失性存储器也叫随机存储存储器,分为动态随机存储存储器和静态随机存储存储器,表现出来的区别在速度上。
动态随机存储存储器,英文缩写写作DRAM,一般每个单元由一个晶体管和一个电容组成。
特点是单元占用资源和空间小,速度比SRAM慢,需要刷新。一般计算机内存即由DRAM组成。在PC上,DRAM以内存条的方式出现。
静态随机存储存储器,英文缩写写作SRAM,一般每个单元由6个晶体管组成,但近来也出现由8个晶体管构成的SRAM单元。
特点是速度快价格贵,单元占用资源比DRAM多,在PC上,一般CPU和GPU的缓存即由SRAM构成。
2、摩尔定律
摩尔定律(英语:Moore's law)是由英特尔(Intel)创始人之一戈登·摩尔提出的。
其内容为:集成电路上可容纳的晶体管数目,约每隔两年便会增加一倍;经常被引用的“18个月”,是由英特尔首席执行官大卫·豪斯(David House)提出:预计18个月会将芯片的性能提高一倍(即更多的晶体管使其更快),是一种以倍数增长的观测。
上世纪末开始,随着CPU的性能极速发展,矛盾出现了。CPU运行的太快了,而磁盘的数据读取又太慢了。
经过上一节的介绍,我们发现并没有一种可以同时满足稳定可用、支持持久化保存、存取速度要非常快、成本便宜并且容量大体积小的存储器,那就各取其长,将各类存储器,根据其各自的特点,按一定规律组合起来使用。
后来,CPU的性能达到物理极限而引出的计算机多核多级缓存架构,本文暂不展开。
3、存储层次
百科如是说明:存储层次是在计算机体系结构下存储系统层次结构的排列顺序。每一层于下一层相比都拥有较高的速度和较低延迟性,以及较小的容量。大部分现今的中央处理器的速度都非常的快。
大部分程序工作量需要存储器访问。由于高速缓存的效率和存储器传输位于层次结构中的不同等级,所以实际上会限制处理的速度,导致中央处理器花费大量的时间等待存储器I/O完成工作。
简单来说,如果单一存储器无法满足全部的需求,那就通过各类存储器的组合使用,在一定程度上满足了各方面需求都达标的一种存储结构,即存储层次,或称内存的层次结构。
在网上找了这张经典的内存层次结构图
大部分电脑中的存储层次如下四层:
- 寄存器:(应该是)最快的访问。
- 高速缓存(L1-L3:SRAM):高速缓存中的访问速度为纳秒级别,非常快。
第一级高速缓存(L1),通常访问只需要几个周期,通常是几十个KB。
第二级高速缓存(L2),比L1约有2到10倍较高延迟性,通常是几百个KB或更多。
第三级高速缓存(L3)不一定有,比L2更高的延迟性,通常有数MB之大。
第四级高速缓存(L4)不普遍,CPU外部的DRAM,但速度较主存高。
- 主存(DRAM):访问需要几百个周期,可以大到数几十GB。
- 磁盘存储:需要成千上百个周期,容量非常大,持久化保存数据。
这一节中介绍的几种存储器,其各自工作所在的层次一目了然,需要注意的是其中高速缓存不受操作系统管理,我们重点看操作系统可管理的主存,后文为了方便叙述,不做特殊说明时,内存单指上图中的主存。
如今,市面上个人计算机的常规配置是三级高速缓存下配上16G内存和512G的固态硬盘,各层存储器在操作系统的内存管理下协调工作,让我们“同一时间”可以流畅使用多个软件。
4、关于内存分层的思考
计算机为了提高访问速度而采用的内存分层结构,似乎像是互联网公司高性能架构中的常用手段——缓存,其一我们把数据尽可能的放在靠近使用者(CPU)的地方,其二我们使用速度更快但不会持久化数据的中间件来做数据库(磁盘)的缓存(内存)。
这就是要去学习基础知识和阅读经典论文的原因,我们会发现思路都是相通的,落地方案的差异也不过是考虑到了特定领域与业务。
四、内存管理的需求和实现
1、需求确认
现在,基于以上内存的硬件结构,我们操作系统要完成对内存管理的能力,它主要应该具备如下的能力:
- 抽象:逻辑地址空间,屏蔽掉真实的内存地址,在多道程序环境下,程序中的逻辑地址与内存中的物理地址不可能一致,因此存储管理必须提供地址变换功能,把逻辑地址转换成相应的物理地址。
- 分配与回收:即内存空间的高效率分配和及时回收。
- 安全与隔离:独立地址空间,保护各自进程的保护,恶意去破坏其他进程的数据,隔离性,保证各道作业在各自的存储空间内运行,互不干扰。
- 共享:访问相同内存,各进程间的数据传递机制允许多个进程访问同一块公共的内存空间,这是效率最高的进程间通信形式。原本每个进程的内存地址空间都是相互隔离的,但操作系统提供了让进程主动创建、映射、分离、控制某一块内存的程序接口。当一块内存被多进程共享时,各个进程往往会与其它通信机制,譬如信号量结合使用,来达到进程间同步及互斥的协调操作。
- 虚拟化:在无需扩大内存硬件容量的情况下,为了满足同时运行多个程序,我们需要更大的地址空间,也就是虚拟内存。
2、实现方式
操作系统主要是通过下面五种方法实现的:
- 程序重定位
- 分段
- 分页
- 虚拟内存
- 按需分页虚拟内存
这里涉及到的实现方式我们后面会逐一学习,现在先来看程序重定位。
五、程序重定位
1、地址空间
首先,需要了解两种地址空间,物理地址空间和逻辑地址空间,前者是硬件支持的地址空间,也叫绝对地址。
后者是程序看到的,一个运行的程序所拥有的内存范围,也叫相对地址。
操作系统负责建立并维护这个对应关系,我们知道程序的运行,依次需要经过编译、链接、载入(程序重定位)后,才能执行。
其中编译阶段,简单来说就是由“高级语言”转成“机器语言”的过程,该阶段和地址空间关系不大,这里暂时不展开,我们重点看后两个阶段。
2、链接阶段
经过链接阶段后,程序就是拥有完整的逻辑地址空间的可以执行的程序了,比如Windows下的.exe 文件。
三种链接方式:
- 静态链接:在程序运行之前,先将各目标模块及它们所需的库函数连接成一个完整的可执行文件(装入模块) ,之后不再拆开。
- 装入时动态链接:将各目标模块装入内存时,边装入边链接的链接方式。
- 运行时动态链接:在程序执行中需要该目标模块时,才对它进行链接。
3、载入阶段
载入阶段,或称之为装入阶段,由操作系统进行内存地址分配,并将程序的逻辑地址转换为物理地址。
这块同样有三种方式进行载入:
- 绝对装入:在之前的编译阶段,编译程序直接产生绝对地址的目标代码。而后载入程序按照载入模块中的地址,将程序和数据装入内存。那哪种程序环境,可以在编译阶段就能确认物理地址呢?没错,绝对装入只适用于单道程序环境。
- 静态重定位:编译和链接后的指令中使用的地址、数据存放的地址都是相对于起始地址而言的逻辑地址,在载入阶段可根据内存的当前情况,装入到内存的适当位置。装入时对地址根据初始位置和偏移量进行重定位,将逻辑地址变换为物理地址,地址变换是在装入时一次完成的。也就是说,必须分配其要求的全部内存空间,如果没有足够的内存,就不能装入该作业。
- 动态重定位:现在又称动态运行时装入。编译、链接后的装入模块的地址都是从0开始的。装入程序把装入模块装入内存后,并不会立即把逻辑地址转换为物理地址,而是把地址转换推迟到程序真正要执行时才进行。因此装入内存后所有的地址依然是逻辑地址。这种方式需要一个重定位寄存器的支持。这种方式虽然带来了复杂性,但是却将内存空间,极大极大的扩大了(虚拟内存的作用之一),现在的载入程序一般都是这种方式进行载入。
4、地址空间的读取过程
由前文的了解,我们可以想到 CPU 对某一地址的内容读取,是经过如下几步完成的:
- CPU需要在逻辑地址的内存内容;
- 内存管理单元寻找在逻辑地址和物理地址之间的映射(这里可以做地址安全的检查,确保程序不相互干扰);
- 控制器从总线发送在物理地址的内存内容的请求;
- 内存发送物理地址内存内容给CPU;