2-嵌入式开发总结之--内存模型

   内存可以说是整个计算机系统的记忆中枢,是计算机系统的核心部件,是存储0101这些二进制信息的地方。没有内存,程序就无法运行。因为CPU要从内存获取指令和数据,执行结果也要保存到内存中,就是说程序的指令和数据都加载到内存中,才可以运行。

   如果说CPU是大脑,那么内存就像是一块黑板,需要时可以放各种临时数据、结果,而且可以根据需要随时擦除,换上新的数据。如果要用模型来简化内存,可以用下图来表示:

   

从硬件基础上来讲,内存是行列组成的栅格网络,内容通过行列地址进行选中获取。选中点的零一状态是通过电容来保存的。因为电容存在放电现象,所以为了保证数据不被丢失,内存电路需要不断的充电,以保证数据无误,这就是所谓的内存刷新。需要说明的是,内存的刷新,是硬件自动进行的,对程序透明。其实对于非研究硬件的人,或者偏向于写程序的人来讲,简单了解内存的基础原理就可以了,不必对内存硬件细节过多了解。一方面其实这块比较繁琐复杂;另一方面,对理解程序运行也不会产生障碍。

   因此,为了更好的理解内存在整个程序设计和运行中的作用,说白了就是为了写出更好的程序,为了更容易理解程序的运行,我们就抛开硬件细节,从逻辑和功能上对内存建立一个模型,这个模型不仅简单,而且确实会对我们理解程序运行有相当大的帮助。这个模型可以用下图来展示。

 

   上图可以看作是一个最简单的内存模型。每一个格子代表一个字节。内存是以字节为单位存储数据的,我们就以连续空间的字节序列来抽象出一个逻辑的内存。

   好了,现在有了这个模型,后面所有的与内存有关的讨论都可以使用这个模型来介绍。

   第一 内存里保存的内容

   这里,我们讨论下内存里都存储了什么。如果我们直接说内存里保存的是程序,是指令,是数据,未免显得太过笼统。我们对内存里到底存的是什么还是没有一个感性的认识。实际上,内存的使用很讲究,不是一堆一堆的数据混乱的仍在里面就可以了。不然,那么多程序同时运行,到时候这儿一块,那里一块,根本就无法管理。况且,自从计算机诞以来生,内存空间始终都是紧缺资源,(关于这一点,后面在虚拟内存部分还还会介绍到),也容不得胡乱使用。说了这么多,那么到底内存是怎么使用的呢,其实方法并不难。我们需要注意到,内存有两个特点,其一,就是内存的结构是平坦型的,就像平原一样,一马平川。这是跟其他有结构的部件(典型如磁盘)区别而言。其二,就是内存资源十分有限,这也就是说平坦的优质资源十分有限。这就决定了我们必须对内存的使用做出规划,就像国家对土地的使用一样,分区分块。这一部分做什么,那一部分做什么,都必须根据实际需求来定,不是想当然的,而且大小也要根据实际情况合理确定,既要满足需求,又不能浪费。这也符合人类对事物进行分门别类管理的认知。具体到计算机的内存,微观角度看,设计者们将内存分页、分段来管理,如下图所示:

 

   这里一页按照4096字节大小来表示。为了管理物理页,通常会有页目录以及页表。这些内容会在后面操作系统虚拟内存部分详细介绍。

   宏观角度看,设计者们又根据功能将内存区块分为几个部分,像Linux系统就包括有操作系统部分,磁盘缓冲区部分,显示缓冲区部分,堆栈部分等,如下图所示:

 

   下图我们将上面两部分结合起来,展示一个示例。这里,大家先有一个感性的认识,这些内容在操作系统基础部分有更整体,更详细的介绍。

 

   第二 内存上电需要检查

   操作系统在被加载前,严格来讲,是在 Bootloader阶段会做这项工作。为什么要进行这项工作呢?因为内存不能出错,计算机对内存的错误必须零容忍。这一点应该是很好理解的。简单想象一下,如果上一时刻保存在内存中的指令是将两个数相加,而下一时刻读出来却变成了相乘,计算机还谈何正确工作。既然检查这么重要,那么,具体是如何进行检查的呢?一种普遍的方法是写一个固定的数据到内存中,然后再读出来,比较数据是否发生改变。通过这种方法,基本可以确定内存是否能够正确的读写数据。当然,因为本身就是检查内存的,所以检查程序一般是用汇编语言来编写,并存储在BIOS中。不管怎么讲,这里说的检查还是软件的检查,具有一定的局限性,不能解决运行过程中产生的硬件错误。其实真要产生了硬件错误,那直接死机得了,也用不着作啥处理(硬件可以带简单的奇偶校验)。后面我们在操作系统基础部分将看到,将内存的使用划分页段,区分操作系统部分和应用程序部分,一部分原因也是为了更好的容错。

   第三 内存操作单位是字节

   在内存检查通过后,CPU就不断的访问内存,不论是读取还是写入操作。从内存的基础模型可以看出,内存的基本操作单位是字节。一个字节由八个二进制比特位构成。关于一个字节为啥是八个比特,以及内存为啥要以字节为单位,这些是由计算机工作的数学基础及工程实践决定的,我们知道就好,不需要钻牛角尖非要问出个为什么。当然了,一个字节也可以规定为九个比特,内存也可以以多个字节大小为一个单位,这样重新设计一套新的计算机也是可以的。好了,言归正传,既然内存访问单位是字节,那么是不是CPU每次读写就只能是一个字节呢?显然不是这样的。回顾计算机发展历史,相对于其他领域,计算机系统的发展可谓是腾云驾雾,飞跃式发展的。其革新换代,真正是一年一个样。著名的摩尔定律就是最好的证据。从最初的四位CPU到如今的六十四位CPU,CPU内部各方面可以说都发生了巨变。而以CPU发展为标志,引领的计算机整个系统的发展也是一日千里。虽然CPU发展到六十四位了,但内存基本操作单位还是一个字节没变。虽然CPU能够访问字节级别的数据不假,但是并不是说CPU每次就只能读写一个字节。对于六十四位CPU而言,每次读写的位宽是六十四位,也就是说,每次操作八个字节,没错,就是八个字节。这个大小其实是由数据总线的宽度决定的。对于六十四位CPU,每次操作六十四位也就是八个字节,效率最高。这种情况下,CPU如何做到字节级别的访问呢?其实很简单,读取就不用说了,就算读出来八个字节,CPU也只用其中的一个字节,其他无关的扔了即可。写入呢,操作就稍微复杂点,但是原理也很简单,CPU只对需要修改的字节进行改动,其他字节不动,写入时原样写入八个即可。概括来讲就是,可以实现,但效率不高。如下图所示:

 

   即使只是读取或修改上图中蓝色区域的一个字节,数据总线上也总是要流动整个橙色区域包裹的八个字节。

   其他位宽CPU的读写原理也是类似的。了解了这些细节过程,这就产生下面两个程序设计中实实在在不可回避的问题。

   第四 内存字节序的问题

   关于内存字节序问题,我们在很多技术资料中,常会看到,常常也被称作大端和小端问题。大小端其实是个CPU的问题,因为是由CPU来决定的,不是内存决定的,但是问题的表现跟内存关系却更大。

   当我们定义了一个整形(假设为四个字节大小)变量,它的值为0x12345678,那么这个变量的值在实际内存中是如何存储的呢?这有下面两种方式:

 

   上图中左边为大端字节序(Big-Endian),右边为小端字节序(Little-Endian)。在编写跨平台或者是网络程序时,需要特别关注字节序问题。否则,两边的系统对数据的理解可能不一致。

   关于字节序不展开介绍了,这属于基础中的基础内容。这里介绍一种快速记忆方法,叫高高低低,这是当年大学时候,老师教的。满足高高低低的就是小端字节序。高高低低是啥意思呢?就是高地址存放高位数据,低地址存放低位数据。大家对应到上面的图,内存地址从上到下递增,是不是就是右边的情况?答案是显然的。

   第五 字节对齐问题

   字节对齐是因为我们有时候会定义一些类似结构体的组合数据,从而方便程序开发和阅读。这就难免存在这些组合数据的各个部分大小不满足CPU总线的整数倍。以三十二位小端字节序CPU为例(四字节对齐),当我们的组合数据是下面这种情况时,内存中的数据实际是按照下图存放的。

{

    int part1; //0x78563412

    char part2; //0xab

    long long part3; //0x55443322119aefcd

}

   可以看到,为了满足字节序要求,编译器并没有将0xcd紧挨着0xab存放。当我们访问part3的时候,CPU可以以字节对齐的方式读出part3的内容,而不会存在错位的情况。否则,CPU需要三次总线访问才能够获取到part3的全部内容。

   上面是性能问题。仔细观察上图,我们还可以看到,这种情况下内存占用比紧凑模式要多一点。当我们用上面组合体的大小参与一些处理时,比如内存拷贝,就要特别注意这种空位存在的情况,这次就是正确性问题了。

   第六 简单的例子

   最后,我们通过一个例子来将逻辑结构性很强的程序数据内容与内存简单的线性保存结构对应起来,从而加深理解。举这个例子,是为了对应开篇提到的,把内存当做黑板和演算纸。

   我们以链表举例。如下图所示,内存中存储有节点的地址,逻辑上节点是首尾相连的,但是在内存中,只是存储了他们的地址。比如节点1存储了节点2的地址,通过节点1获取节点2的地址并找到节点2的内容。节点2的内容中存储有节点3的地址,通过节点2找到了节点3。节点3中存储有节点4的地址......以此类推,我们通过内存实现了一个链表结构。当我们理解这个结构时,关注的是其首尾相连的特性,而非内存中的地址。链表这个首尾相连的特性是我们在黑板和演算纸上表达的,而实际内存中只有数据,包括地址数据。我们在黑板和演算纸上并不关注这个地址,而是更关注地址对应的连接关系。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙赤子

你的小小鼓励助我翻山越岭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值