1. 前记
我们知道,不同的计算机结构对RAM 的使用方式是有区别的,典型的计算机结构有两个,冯诺依曼结构和哈佛结构,而两大阵营的领军人物就是传说中的Intel X86系列的8086和51单片机系列的8051。请先对号入座,不理解的跳过去,继续往下看。
2. What?
长啥样?
内存条,RAM中的一种,常见的应该是DDR SDRAM。相信各位都触摸过它,冷冰,无情,当然,你上电后它就变了样,暖暖的,无怨无悔的为我们干活。
嘿!别唬我,这个俺知道,51单片机。没错,51单片机里面也内置了RAM ,叫片内RAM。
这个就是一般嵌入式板卡上的RAM, 为了加以区分,就叫片外RAM。
广义上讲,CPU内部的寄存器也算是RAM的一种。
在哪里?
PC主板图片
嵌入式板卡图片
找一找你系统上的内存吧,再看看CPU datasheet 上有没有提到内置RAM。
特点是啥?
RAM(Random Access Memory),随机存储器。特点如下:
- 如其名,可随时读写
- 快,读写速度杠杠的,CPU最喜欢和它一起“玩”了
- 掉电后,数据全部丢失,因此,别指望RAM中的数据长期存储,那么,我想要长期存储公司所有员工的数据怎么办?用FLASH吧。
请参考:
与CPU的连接方式
RAM是用来存储CPU计算所需的数据的,那么它怎么与CPU进行通话呢。就是通过那个叫做大巴士(Bus)的东东了,所有数据都是坐着大巴士在CPU和RAM 之间飘过来,再飘过去的。
3. Why?
RAM特点决定了它的应用,保存计算过程中的数据。什么?不理解,举个例子,我想知道1+2等于多少?条件,写个程序,这活儿让必须由CPU干,看看过程:
- CPU向RAM单元里“写入”两个操作数1和2(“写入”过程先飘过)
- CPU从RAM指定单元中“读取”操作数1,操作数2,和运算指令“+”(“读取”过程也先飘过吧)
- CPU解析读取的指令
- CPU执行计算,给出结果3
执行过程中的“1”“2”“+”就是存储在RAM中的(当然可以用更简单的方法实现),执行完后,RAM中数据就没有存在的意义了,可以去干2+3等其他活了。这么简单的活儿当然没有必要CPU来完成,但是,如果领导非要你去确认一下1+1计算4294967295次这个“哲学”问题的结果的话,总不能找个小本本自己蛮干吧。看着就头大,可工作吗,总不能有情绪啊。
写个程序,扔给计算机,这哥们可是位任劳任怨的好公仆,喝杯茶,你就可以认真负责的交差啦。
好了,现在我们知道CPU虽然是个好同志,可是总有忙不过来的时候,那就请个秘书(存储器)帮忙吧!我们的存储器有很多种,寄存器,RAM系列,ROM系列,flash系列,还有硬盘,USB,SD卡(这哥仨比较特殊,请先记住,他们可读可写,掉电后数据不会丢失,先归到FLASH系列吧)等等。
那么,为什么CPU会选择RAM呢?我们先聊聊其他的。
为什么不是寄存器呢?前面提到过寄存器也是一种RAM,而且读写速度比片外的RAM还快。但是,寄存器是内嵌在CPU内部的,CPU就那么大点儿,能够内嵌的寄存器就那么几个,有的还有特殊用途,不能随便玩,仅用这几个存储单元,对CPU进行的复杂运算就无能为力了。
为什么不是ROM呢?从其特点看,这玩意儿相当稳定,Read Only嘛,轻易改变不了上面的内容,这也是为什么很多嵌入式系统将代码烧到ROM的原因了,随你风吹雨打,我自岿然不动。但是,你只让CPU看,不让CPU摸,CPU怎么把中间结果暂存到里面呢?
为什么不是FLASH呢?FLASH是可读可写的,这点与RAM没区别。但是,FLASH的写比较麻烦,不能直接写,只能先把原来的内容擦干净。CPU也是嫌麻烦的,多出来的擦过程意味着,CPU干正常活的时间变少了。不爽。还有,FLASH的读写是以块为单位的(NOR FLASH 块大小64~128KB,NAND FLASH块大小,8~32KB),这可不行啊,我就想用一个字节,影响太大了。而且,FLASH写入擦除速度也不敢恭维(NOR FLASH ,5s;NAND FLASH,4ms)。
再看看RAM吧,速度,价格,好而不贵,这里需要掌声!
种类 |
速度 |
单位存储价格 |
寄存器 |
|
|
ROM |
|
|
NAND FLASH(FLASH) |
|
|
SDROM(RAM) |
|
|
CPU |
|
|
上面的数据是同一板卡上各芯片的数据,因为技术发展得太快,不同时期的比较没有参考意义。
CPU在找“秘书”的时候郁闷了,Why?Why?Why there is always a “但是”,肿么办?看看价格,再看看速度,其他的东西都不理想,好吧,就是你了,瞎看什么,说你呢内存。
CPU选择存储器,这过程有点儿像“木桶”现象,CPU速度往往很快,因此,制约系统速度的那个短板就是存储器了。而随着技术的发展,单位存储的成本也在不断下降,相信,总有一天,现有存储器的区别将不再这样明晰,继而,从这个世界消失。一切只是时间而已。
4. HOW?
CPU搭讪RAM
我们知道CPU和RAM是一对好基友。那么,CPU总得主动去搭讪吧。
存储器内部被划分为一个个的小格格,并且从0开始编号,例如,内存的容量是64K,那么就有64*1024个小格格啦。每个小格格可以用来读写1、2、3等等的数据。CPU如何操作呢?还记得大巴士嘛。三步走:
- CPU通过地址总线,向存储器发送指令的地址信息
- CPU通过控制总线,向存储器发送控制信息(读OR写)
- CPU通过数据总线,从存储器读取数据或向存储器写入数据
我们来看看CPU从3号单元中读取数据的过程。(摘自:《汇编语言》)
- CPU通过地址总线将地址信息3发出,我们可以看到,地址信息3就乘着巴士飘向RAM了
- CPU通过控制总线发出读取命令,我们可以看到,“读取”信息乘着巴士也飘香RAM了
- 存取器将3号单元中的数据8通过数据总线传回CPU,我们可以看到,数据信息8乘着数据巴士飘向CPU了。
是CODE还是DATA?
我们知道了CPU如何从RAM获取数据。那么CPU从存储器上得到数据后,这个数据是指令还是数据呢,如何区分呢?
其实,在存储器内部,指令和数据没有任何区别,都是二进制信息。CPU在发送读取命令前,会先明确,收到的数据是指令还是数据(CPU的主观想法),换句话说,CPU在读取数据前,告诉自己,收到的这个数据将作为指令,收到数据后,就把该数据解析为指令,并存储在CPU的指令缓存器中;如果在读取数据前,告诉自己,收到的数据将作为数据,那么,收到数据后,就把该数据解析为数据,存储到指定的寄存器中。
例如,存储器中的二进制信息1000100111011000,计算机可以把它看做大小为89D8H的数据来处理,也可以将其看做指令mov ax,bx来执行。
1000100111011000→89D8H(数据)
1000100111011000→mov ax,bx(指令)
CPU是怎么工作的?
请参考《汇编语言》 第2.10节 CS和IP
画图解释1+2过程,注意应该使用SS,SP,PUSH,POP。
传奇的冯诺依曼结构计算机
冯·诺依曼结构,又称为普林斯顿体系结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构。取指令和取操作数都在同一总线上,通过分时复用的方式进行。不理解???来,看图解释(Intel 8086 CPU)。
8086是16位结构的CPU,它的寻址能力是216=64KB(补充,其实8086的地址总线是20位的,也就是说最大的地址范围是220=1MB,但是由于16位CPU一次只能处理16位数据,所以才是64KB的寻址范围,可见,总有拖后腿的~)。
冯氏结构认为,读取指令和数据并没有区别,所以虽然所有的存取器在物理上是独立的,但CPU对他们进行统一编号,在它眼里只有一个逻辑存储器。为什么这么搞?简单啊,CPU只要记住各段地址映射到哪个物理存储器就好了,然后就是发重复的读写命令就OK!
上图中虚拟内存空间各段地址分配如下:
地址0000H~7FFFH的32KB空间:指向内存条(RAM)
地址8000H~9FFFH的8KB空间:指向显存
地址A000H~FFFFH的24KB空间:指向各个ROM的地址
其实,冯氏结构还有一个好处, CPU封装简单,通俗讲就是管脚少,因为和所有存取器连接的地址总线,数据总线,控制总线是共用的。试想,如果CPU与每个存储芯片都有一套独立的地址总线,数据总线,控制总线,那么要想访问所有存储器芯片,CPU引脚最多将是现在的N(N为存储器个数)倍。
目前使用冯氏结构的CPU和微控制器有很多。其中包括英特尔公司的8086及其他CPU,TI的MSP430处理器,ARM公司的ARM7,MIPS公司的MIPS处理器。
怎么又冒出来个哈佛结构计算机?
按冯氏结构的构想,读指令和读数据的操作是分时复用的,但,这里有个严重问题,就是“读指令”的时候,“读数据”就得等着,“读数据”的时候,“读指令”的操作也得先等着。在通用计算机(PC机)上没有问题,但是对于一些像数字信号处理这样的应用来说,这是致命的问题。怎么办?好办,指令和数据分开就可以喽。下面是哈佛结构。
如图,哈佛结构的计算机由CPU、程序存储器(ROM或FLASH)和数据存储器(RAM)组成,程序存储器和数据存储器采用不同的总线,从而提供了较大的存储器带宽,使数据的移动和交换更加方便,尤其提供了较高的数字信号处理性能。
目前使用哈佛结构的中央处理器和微控制器有很多,除了Microchip公司的PIC系列芯片,还有摩托罗拉公司的MC68系列、Zilog公司的Z8系列、ATMEL公司的AVR系列和安谋公司的ARM9、ARM10和ARM11。
为什么很多嵌入式系统采用哈佛结构?除了指令的读取速度,还有一个重要因素要考虑的:稳定。下面是常见的场景:
我们的Windows电脑死机后:骂娘,重启,还没听说过有人去找微软理论吧~
我们的嵌入式系统死机后:停产,而且,这是会使人的…
为什么会死机呢?其实很多时候是我们的“不小心”,操作数据的时候不小心把指令改掉了,而如果程序和数据分开存储呢,系统会稳定很多。
1+2=?再算一遍
我们先这样假设,我们计算的程序代码(CODE)是“1+2=?”数据是(DATA)“1”“2”。(当然,这样的假设并不严谨,只是这样更便于理解)
典型冯氏结构,1+2执行过程:执行前,CODE+DATA就是一个文件,其实就是一个EXE文件,存储在硬盘里。当我们双击这个文件时,CPU将硬盘中的CODE+DATA拷贝到RAM中,并指向第一个指令,CPU开始执行。想想我们在Windows上打开Notepad.exe的过程吧,查看你的内存使用量是不是突然增加了一小块,因为Notepad.exe是要被拷贝到RAM中才可以执行的。
典型哈佛结构,1+2执行过程:上电前,CODE+DATA就是一个文件,通常叫做Firm,被烧写到ROM或FLASH中,上电后,CODE部分不动,CPU将ROM中的DATA部分拷贝到RAM中,此时,程序存储器(ROM或FLASH)中存储CODE,RAM中存储着DATA,此后,CPU分别读取CODE和DATA,在CPU内部去执行读取到的指令。
注意,我们这里说的是都是“典型”结构,因为现在2015年,嵌入式系统为了提高读取指令的速度,也会把所有的CODE+DATA拷贝的RAM中执行,毕竟,RAM的读取速度比ROM和FLASH都快嘛,这样的哈氏结构就有点儿像冯氏结构了。另一方面,Intel X86系列的CPU内部加入了指令和数据缓存,从效果看,已经可以同时读取指令和数据了,这样的冯氏结构,又有点儿像哈氏结构了。
因而目前大部分计算机体系都是CPU内部的哈氏结构+CPU外部的冯氏结构。这样各取所长,达到了一个平衡。
CODE在哪儿?
从上面的讨论中,我们可以知道,这个问题是要看计算机状态的。
|
典型冯氏结构 |
典型哈氏结构 |
运行前 |
硬盘,想想咱们的应用程序是不是在某个磁盘下。确切的说,是EXE文件中的CODE部分。 |
ROM或FLASH中的CODE部分 |
运行时 |
RAM里,是由CPU从硬盘里拷贝过来的 |
ROM或FLASH中的CODE部分 |
DATA在哪儿?
与CODE一样,这个问题是要看计算机状态的。
|
典型冯氏结构 |
典型哈氏结构 |
运行前 |
硬盘,想想咱们的应用程序是不是在某个磁盘下。确切的说,是EXE文件中的DATA部分。 |
ROM或FLASH中的DATA部分 |
运行时 |
RAM里,是由CPU从硬盘里拷贝过来的 |
ROM或FLASH中的DATA部分 |
RAM全干了什么?
RAM是一种存储器,它的作用当然是存储数据了。我们来看看RAM到底可以存储哪些数据。
- 存储计算的过程数据,我们可以这样理解,在C语言中声明的临时变量就在这里,如1+2=?中的1和2
- 存储常量数据,这些数据是不变的,比如软件的版本号信息
- 存储程序代码,前提是,程序运行时才会将程序代码拷贝到RAM中,程序关闭后被释放
- 存储内核代码(就是传说中的操作系统了),开机时,从ROM或FLASH中拷贝到RAM中,并长期驻留在RAM中,直到关机
RAM内部细分?
先看图:
由上面的讨论RAM的作用可以知道,内存可以按功能来细分,各个系统的叫法是有一点点儿区别的,请注意区分:
- 代码区:简单的嵌入式系统,这个区只有类似1+2=?这样的代码。然而,像Windows和Linux这种含有操作系统的计算机,这个区包含内核代码和应用程序代码的。
- 数据区:应用程序使用的数据,这个是可以再细分的
- 堆栈区:这个单独聊
请谨记,这里所说的细分,只是我们使用内存的一种方法,本质上它仍然只负责存储数据,这样区分,只是概念性的。
堆栈
说起这个,得用一火车的话…
没有火车怎么办?简单聊。
目前为止,我们已经知道堆栈是RAM的一部分,他们依然是用于存储数据的,而且是人为划分的,那么,来看看他们的不同点吧。
|
栈(stack) |
堆(heap) |
谁分配的? |
编译器和操作系统 |
编译器和操作系统 |
分配多大? |
由你的程序决定,程序编译完后,大小就固定了 |
由你的程序决定。一支程序编译完后,CODE,DATA,STACK都是固定的,理论上其他的RAM都可以是Heap |
分配的依据是什么? |
MAP文件里记录了Stack的起始结束地址 |
你的程序里有没有malloc和new |
有何用? |
存储局部变量,参数 |
? |
何时用? |
函数调用时 |
申请未知大小的空间 |
怎么用? |
代码中的声明的局部变量,在调用时都会入栈 |
主动申请:malloc和new 主动释放:free和delete |
何时开始? |
程序运行时 |
程序运行时AND程序主动申请 |
何时结束? |
程序结束时 |
程序主动释放OR程序结束时 |
请参考:
What and where are the stack and heap? 里面干货很多,好好读读吧
什么是堆和栈,它们在哪儿? 如果你实在懒得不行,看看这个,是上文的不完整翻译
一只程序有多大?为什么?
这里指的是,PC上的EXE文件,嵌入式的FIRM文件大小。前面我们讨论过,你的程序中CODE,DATA, STACT大小是固定的,而且是编译后就决定了,因此你的程序大小由你程序中使用的CODE,DATA, STACT大小决定。
一只程序可用内存多大?为什么?
这个还用问吗?当然是RAM有多大就可以用多大。这在嵌入式系统中没有问题,你有一片128MB的RAM,自然就拥有了128MB的使用权。但在PC上你不觉得奇怪吗?假设你的PC上安装的是Windows7 32-bit OS,插着一条4GB的内存条,那理论上,最大的RAM寻址空间是232=4GB,你安装了Office2010。
我们来这样操作一下,打开一个Word文档,我们可以肯定,Word可用的RAM是这个内存条的4GB空间。我们再打开一个Excel文档,Excel可用的RAM是多少?答案是:理论上也可以有4GB。
为什么?现在的操作系统都支持多进程(Windows和Linux都支持),每个应用程序就是一个进程,对于一个进程而言,进程独享整个内存。其实,操作系统搞了个虚拟内存的东东,我们说内存独享整个内存,是指独享整个虚拟内存(虚拟内存会保持与物理内存的映射关系)。为什么这么干?每个程序(进程)是可能占用很大内存的,为什么说可能呢,因为Heap的动态变化。而通常情况下,这支程序(进程)内存的占有量并不大,过百M的程序已经算是内存大户了。如果进程占用整个物理内存,别的进程就别玩了。
所以,现在的策略是每个进程独享虚拟内存,多个虚拟内存共享物理内存,这样,大家就可以一起快乐的玩耍啦!
请参考:
MAP文件作用
查查你的MAP文件就知道了,它是记录你的程序,在RAM上的地址信息的。
5. 后记
我们回到原点,回答问题。
WHAT:RAM是个读写速度很快的存储器;
WHY:从速度和成本考虑RAM最适合于与CPU协同工作;
HOW:不同的计算机结构对RAM的使用方式是不同的。
不同系统往往要考虑两件事:成本,速度。从上面的讨论中可以看出,每个体统在选择芯片以及结构的时候,都在权衡这两件事,只是侧重点不同而已。记住,这两个不是东西的东西很重要,它们最终决定了你的系统的样子。
6. 参考文章:
. 汇编语言 王爽 第二版
. What and where are the stack and heap?
. 单片机程序程序存储空间(ROM)和数据存储空间(RAM)详解
. C语言内存分布图
来源:https://www.cnblogs.com/iluzhiyong/p/5107523.html