内存管理(指令,地址,装入,链接等)

什么是内存,有何作用

存储单元

内存地址

在这里插入图片描述
在这里插入图片描述
以上两张图呢,其实就生动形象可以来描述内存,内存是什么呢?其实内存就是用于存放数据的硬件。程序执行前,需要先把程序放到内存中才可以执行。

在这里插入图片描述
从上面这张图中呢,就可以生动形象的理解什么是内存。
内存简单可以理解为:地址+存储单元
好比我们进行住宿的时候,如何可以知道住在哪呢,那就会有房间号,通过房间号,就可以知道住在几楼,在哪个房间,所以说呢,这个时候,在内存中其实也是一样的,也可以通过地址可以找到存储数据的地方,那就是存储单元。内存中呢也有一个个的小房间,每个小房间可以理解为就是一个存储单元。

计算机可以有多种编地址方式:

  • 如果计算机按照字节编址,则每个存储单元大小为1字节,即1B,即8个二进制位
  • 如果字长为16位的计算机按字编址,则每个存储单元为一个字,每个字的大小呢,即为16个二进制位。当然也会有32位和64的,这也就是我们平时所提到的32位操作系统和64位操作系统。

了解一下常用的数量单位:
在这里插入图片描述

进程运行的基本原理,内存起到什么作用

指令的工作原理

进行的运行原理->指令

此图片来描述指令执行过程
我们写的代码要翻译成CPU能识别的指令。这些指令会告诉CPU应该去内存的哪个地址 存取数据。
这个数据应该做什么样的处理。在上面这个例子中呢,指令中直接给出了变量x的实际存放地址(物理地址),但实际在生成机器指令的时候,并不知道该进程的数据会被放到什么位置。所以编译生成的指令中一般是使用的逻辑地址(相对地址)

指令存储在程序段中的。
对上图有更深刻的了解的话,也需要了解关于CPU的体系结构的相关知识。简单说一下:CPU主要包含运算器和控制器两大元件,当然也离开不了计数器,通用寄存器等器件
了解CPU的核心元件,可以通过此链接来进行了解

上面这张图呢,可能也需要了解的就是进程包含的内容,主要分为三部分

  1. 程序段
  2. 数据段
  3. PCB(程序控制模块)
    在上面图中也有进行表示

逻辑地址vs物流地址(绝对地址)

其实这个逻辑地址和物理地址也很容易理解
逻辑地址呢就可以简单理解为打标签 每个人都有一个身份证,这个身份证🆔就是一个标签,来标识唯一性
物理地址呢,也成为绝对地址,就是这个人具体居住在什么地方,就可以理解为你所居住的地点,在房管局进行备案的地址,通过此地址就可以找到你.
Eg:宿舍四个人一起去旅行,四个人的学号为0,1,2,3
住酒店的时候呢,给他们安排了4个房号相连的房间。四个人按学号递增次序入驻房间。比如 0,1,2,3号同学分别入住了5,6,7,8号房间。
四个人的编号0,1,2,3其实就是个相对位置,也就是逻辑位置,而每个人入住的房间呢就可以称为绝对位置
只要知道0号学生入住的哪个房间,就可以知道1,2,3号同学入住的房间是哪一个,因为 他们也都是有规则存在的,这里呢就是递增方式来进行存储。
如果0号同学入住的房间号是N号,那M号同学入住的房间号呢,那肯定是N+M号房间。
也就是说,只要知道各个同学的相对位置和起始房号,就一定可以计算出所有同学的绝对位置。 可以简单理解就是相对位置+起始房号就是这个同学所入住的房间
在这个例子中的规则定义。
如下图所示:
在这里插入图片描述

从写程序–>程序运行

从写程序到程序运行呢,其实会经过很长的一个过程,学过高级语言的或者了解过高级语言的,我们都应该比较清楚的是,比如Java语言,就需要经过编译,然后解释程序来对进行进行解释等后续的链接装入等一系列操作,每一步的程序都是有其需要深究的意义所在,这也就是我们所需要掌握的原因,这样我们才可以更加清晰的理解清楚,为什么要这么做,这么做的原因是什么,来解决什么问题,这个问题是如何解决的,其实就可以更加清楚的认识到内存在其中起到了什么作用。

下图是简单的高级语言到程序运行的转换流程图:
在这里插入图片描述
对上述的三个名词作下解释:

  1. 编译:由编译程序将用户的源代码编译成若干个目标模块(编译其实就是把高级语言翻译为机器可识别的语言,也就是机器语言)
  2. 链接:由链接程序将编译后的一组目标模块,以及所需要库函数链接在一起,形成一个完成的装入模块
  3. 装入:由装入程序将装入模块装入内存运行
    如果对Java高级语言有了解的,其实就间接的可以思考或者理解一下JVM在整个过程中是如何去做的这件事情,就会发现有异曲同工之妙
    当然C语言也不例外,其实C也提供了编译环境,编译程序,所以大致的流程都是相同的。
    到最后都是装入到内存中,才能进行程序的执行的。内存就起到了至关重要的作用,其实可能很多有人会去问,为什么非要存储到内存中才可以执行,会有这样的疑问?其实思想也是比较简单的,这可能要了解CPU和SRAM和DRAM的前世今生,以及CPU和内存以及磁盘的发展演变过程,其实都大致遵循了摩尔定律,就会对这个疑问有深入的理解了。有兴趣可以了解一下。
    SRAM和DRAM的前世今生
    关于CPU和内存的发展演变以及为什么会出现SRAM?又为什么需要内存的存在

三种装入方式

装入模块装入内存
在这里插入图片描述
指令1 就可以理解为装入模块获取到的物理地址中的起始地址+80就等于是物理地址,往80的位置写入1,0是起始地址,80是逻辑地址。在物理地址中,就可以知道往哪个存储单元中进行存放数据。
可以简单这么通俗易懂的去理解装入程序去做的事情。
装入分为三种装入方式:

1.绝对装入(Absolute Loading Mode):

在编译时,如果知道程序将驻留在内存的什么位置,那么,编译程序将产生绝对地址的目标代码。即按照物理内存的位置赋予实际的物理地址。例如,事先已知用户程序(进程)驻留在从R处开始的位置,则编译程序所产生的目标模块(即装入模块)便从R处开始向上扩展。绝对装入程序按照装入模块中的地址,将程序和数据装入内存。装入模块被装入内存后,由于程序中的逻辑地址与实际内存地址完全相同,故不须对程序和数据的地址进行修改。程序中所使用的绝对地址,既可在编译或汇编时给出,也可由程序员直接赋予。这个方式的优点:是CPU执行目标代码快。

缺点:1)是由于内存大小限制,能装入内存并发执行的进程数大大减少
2)编译程序必须知道内存的当前空闲地址部分和其地址,并且把进程的不同程序段连续地存放起来,编译非常复杂。由于程序

   因此,通常是宁可在程序中采用符号地址,然后在编译或汇编时,再将这些符号地址转换为绝对地址。

   如何把虚拟内存地址空间变换到内存唯一的一维物理线性空间?涉及到两个问题:
    一是虚拟空间的划分问题。
    二是把虚拟空间中已经链接和划分好的内容装入内存,并将虚拟空间地址映射内存地址的问题。即地址映射。
    地址映射就是建立虚拟地址与内存地址的关系。

2.静态重定位(Relocation Loading Mode)

绝对装入方式只能将目标模块装入到事先指定的位置。在多道程序环境下,编译程序不可能预知所编译的目标模块应放在内存的何处,因此绝对装入方式只适用于单道程序环境。
这句话如何进行理解呢,绝对装入,是事先就知道内存地址的,可以由程序员来进行指定,因为是一个程序在跑,所以,这个时候可以进行制指定。
但是问题来了,如果是多道程序环境呢,多道程序在跑着,这个在内存中驻留的位置如何来确定呢,如何去指定呢?你想去指定这个位置,但是呢,其他程序可能也会往这个位置上去写,这种情况肯定是不能被允许的,所以这个时候,就引发出了新的思考,那就是静态重定位的方式。通过这种思想来解决多道程序环境的问题,这也就是多道程序所引发的新问题,装入方面的问题。
在多道程序环境下,所得到的目标模块的起始位置通常是从0开始的,程序中的其他地址也都是相对于起始地址计算的,此时应采用可重定位装入方式,根据内存的当前情况,将模块装入到内存的适当位置。起始这就是静态重定位的思想所在!

我的理解就是,仅仅是在绝对装入的思想上,做了一些思想上的升级而已,可以结合我们平时项目的开发,之前的设计满足于当时的适用场景,但是随着需求的不断增加以及改变,之前的那一套不适用于这次的功能了,这个时候,就要对之前设计的进行方案设计。或者说,之前的设计,性能有瓶颈,或者是QPS TPS达不到一定的程序,这个时候呢,就要对技术实现方案进行改造,这个时候就需要进行方案升级,通过新的思想和技术方案来解决当前问题,其实套到这个静态重定位中,道理是一样的,绝对装入,解决不掉当前的多道程序的问题,就需要方案上的改变,然后通过静态重定位方式来解决这个问题,当然,引入一个新的技术方案出来,肯定也会产生新的问题。哲学上,就有提到,新事物的产生,必然会引发新问题的产生。所以说在设计的时候,就要解决这种问题,并进行规则的设定。

静态地址重定位:即在程序装入对目标代码装入内存的过程中存在,是指在程序开始运行前,程序中指令和数据的各个地址均已完成重定位,即完成虚拟地址到内存地址映射。地址变换通常是在装入时一次完成的,以后不允许在改变。
值得注意的是,在采用可重定位装入程序将装入模块装入到内存后,会使装入模块的所有逻辑地址与实际装入内存的物理地址不同。
在这里插入图片描述
例如,在用户程序的 1000 号单元处有一条指令LOAD 1,2500,该指令的功能是将 2500 单元中的整数 365 取至寄存器 1。但若将该用户程序装入到内存的 10000~15000号单元而不进行地址变换, 则在执行11000号单元中的指令时,它将仍从 2500 号单元中把数据取至寄存器1而导致数据错误。由图4-3 可见,正确的方法应该是将取数指令中的地址 2500 修改成 12500,即把指令中的相对地址 2500 与本程序在内存中的起始地址 10000 相加,才得到正确的物理地址12500。除了数据地址应修改外,指令地址也须做同样的修改,即将指令的相对地址 1000 与起始地址 10000 相加,得到绝对地址 11000。

优点:无需硬件的支持
缺点:1)程序重定位之后就不能在内存中搬动了;
2)要求程序的存储空间是连续的,不能把程序放在若干个不连续的区域中。

3.动态重定位(动态运行时装入方式 Dynamic Run-time Loading)

可重定位装入方式可将装入模块装入到内存中任何允许的位置,故可用于多道程序环境。但是这中方式并不允许程序运行时在内存中移动位置。因为,程序在内存中移动,意味着它的物理位置发生了变化,这时必须对程序和数据的地址(绝对地址)进行修改后方能运行。然而,实际情况是,在运行过程中它在内存中的位置可能经常要改变,此时就应采用动态运行时装入的方式。

动态地址重定位:不是在程序执行之前而是在程序执行过程中进行地址变换。更确切的说,是把这种地址转换推迟到程序真正要执行时才进行,即在每次访问内存单元前才将要访问的程序或数据地址变换成内存地址。动态重定位可使装配模块不加任何修改而装入内存。为使地址转换不影响指令的执行速度,这种方式需要一个重定位寄存器的支持,

优点:1)目标模块装入内存时无需任何修改,因而装入之后再搬迁也不会影响其正确执行,这对于存储器紧缩、解决碎片问题是极其有利的;
2)一个程序由若干个相对独立的目标模块组成时,每个目标模块各装入一个存储区域,这些存储区域可以不是顺序相邻的,只要各个模块有自己对应的重定位寄存器就行。
之所以进行重定位寄存器的引入的话,

我的理解是:因为在运行阶段,需要动态实时的来进行地址的来回切换,而且每个资源模块的地址也可能在运行阶段会发生改变,这个时候,运行阶段,是非常频繁存在的,引入寄存器,可以在切换后对于相对应的指令或者地址标记到寄存器中,得以来提升整体的性能。自己的理解,如果有不对的地方,希望可以指出来。因为对于太底层的相关实现没有了解那么深入,只能停留在现阶段的基础上来去思考这些设计,各位大佬请指出。
缺点:需要硬件支持。

总结:
程序的装入过程即地址变换过程,分为绝对地址装入,静态重定位装入和动态重定位装入。
静态重定位即在装入前虚拟地址已经变换为绝对地址,装入后不再改变。
动态重定位装入,则是在程序运行的时候再进行地址变换,即程序即使在内存内地址发生变化也不会像静态重定位那样受到影响。

三种链接方式

  • 静态链接方式

在目标模块装入内存之前进行链接,链接成一个装入模块(可执行文件)后不再拆开。

  • 装入时动态链接方式

在目标模块装入内存时,边装入边链接。即在装入了一个目标模块后,发现其要调用的目标模块不在内存中,则由装入程序找到相应的外部模块并将其装入内存。

相较于静态链接方式的优势:
1).方便目标模块的修改和更新。
2).便于实现目标模块的共享。

  • 运行时动态链接方式

在程序执行时才将目标模块链接到装入模块中。即在程序的执行过程中,若发现被调用的目标模块未被装入内存时,则操作系统将立即找到该目标模块并将其装入内存,最后将其链接到调用这个目标模块的目标模块上。

较于装入时动态链接方式的优势:
有些目标模块可能并不会被程序执行(错误处理模块),但是装入时动态链接方式会将其链接到装入模块中,这显然是对性能的损害。但是运行时动态链接解决了这个问题。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值