【操作系统】虚拟内存和内存管理

1.虚拟内存

我们知道单片机是没有操作系统的,所以每次写完代码,都需要借助工具把程序烧录进去,这样程序才能跑起来。另外,单片机的CPU是直接操作内存的物理地址。

在这种情况下,要想在内存中同时运行两个程序是不可能的,如果第一个程序在2000的位置写入一个新的值,将会擦掉第二个程序存放在相同位置的所有内容,所以同时运行两个程序是根本行不通的,这两个程序会立即崩溃的。

操作系统是如何解决这个问题呢?

我们可以把进程所使用的的地址隔离开来,即让操作系统为每个进程分配独立的一套虚拟地址,人人都有,大家自己在自己独立的地址运行,互不干涉。但是有个前提就是每个进程都不能访问物理迪比,至于虚拟地址最终怎么落到物理内存里,对进程来说是透明的,操作系统已经把这些都安排的明明白白了。

操作系统会提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来。

如果程序要访问虚拟地址的时候,由操作系统转换成不同的物理地址,这样不同的进程运行的时候,写入的是不同的物理地址,这样就不分钟冲突了。

于是,这里就引出了两种地址的概念:

  • 我们程序所使用的的内存地址叫做虚拟地址。
  • 实际存在硬件里面的空间地址叫做物理内存地址。

操作系统引入了虚拟内存,进程持有的虚拟地址会通过CPU芯片中的内存管理单元(MMU)的映射关系,来转换变成物理地址,然后再通过物理地址访问内存。

操作系统是如何管理虚拟地址与物理地址之间的关系呢?

主要有两种方式,分别是内存分段和内存分页,分段是比较早提出的,我们先来看看内存分段。

2.内存分段

程序是由若干个逻辑分段组成的,可由代码分段、数据分段、栈段、堆段组成。不同的段是由不同的属性的,所以就用分段的形式把这些段分离出来。

分段机制下,虚拟地址和物理地址是如何映射的?

分段机制下的物理地址有两部分组成,段选择子和段内偏移量。

图1 分段机制

虚拟地址是通过段表与物理地址进行映射的,分段机制会把程序的地址分成4个段,每个段在段表中有一个项,在这一项找到段的基地址,再加上偏移量,于是就能找到物理内存中的地址,如图1.

分段的办法很好,解决了程序本身不需要关心具体的物理地址的问题,但他也有一些不足之处:

  • 内存碎片的问题
  • 内存交换的效率低的问题

内存碎片分为两种:

  • 外部内存碎片,也就是产生了多个不连续的小物理内存,导致新的程序无法被装载
  • 内部内存碎片,程序所有的内存都被装载到了物理内存,但是这个程序有部分的内存可能并不是很常使用,这也会导致内存的浪费。

针对外部内存碎片的问题就是内存交换。

分段为什么会导致内存交换效率低的问题?

对于多进程的系统来说,用分段的方式,内存碎片是很容易产生的,产生了内存碎片,那不得不重新swap内存区域,这个过程回产生性能瓶颈。

因为硬盘的访问速度要比内存慢太多了,每一次内存交换,我们都需要把一大段连续的内存数据写到硬盘上。

所以,如果内存交换的时候,交换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿。

为了解决内存分段的内存碎片和内存交换效率低的问题,就出现了内存分页。

3.内存分页

分段的好处就是能产生连续的内存空间,但是会出现内存碎片和内存交换的空间太大的问题。

要解决这些问题,那么就要想出能少出现一些内存碎片的办法。另外,当需要进行内存交换的时候,让需要交换写入或者从磁盘装载的数据更少一点,这样就可以解决问题了。这就是内存分页。

分页就是把整个物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,我们叫页。逻辑分页地址和物理地址的映射关系如下:

图2 分页逻辑地址和物理地址映射关系

地址变换过程

当进程要访问某个墨迹地址中的数据时,分页地址变换机构会自动的将有效地址(相对地址)页号和页内地址两部分,再以页号为索引去检索页表。查找操作由硬件执行。在执行检索之前,现将页号与页表长度进行比较,如果页号大于或等于页表长度,则表示本次所访问的的地址已经超越进程的地址空间。于是,这一错误将被系统发现,并产生一地址越界中断。若未出现越界错误,则将页表始址与页号和页表项长度的乘积相加,便得到该表项在页表中的位置,于是可以从中得到该页的物理块号,将之送入物理地址寄存器的块内地址字段中。这样就完成了从逻辑地址到物理地址的变换。

分页是怎么解决分段的内存碎片、内存交换效率低的问题?

由于内存空间都是预先划分好的,也就不会像分段会产生间隙非常小的内存,这正是分段会产生内存碎片的原因。而采用了分页,那么释放的内存都是以页为单位释放的,也就不会产生无法鬼进程使用的小内存。

如果内存空间不够,操作系统会把其他正在运行的进程中的最近每被使用的内存页面刚才释放掉,也就是暂时写在硬盘上,称为换出。一旦需要的时候,在加载进来,称为换入。所以,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,内存交换的效率就相对比较高。更近一步地,分页的方式使得我们在加载程序的时候,不再需要一次性了都把程序加载多物理内存中。我们完全可以在进行虚拟内存和物理内存的页之间的映射之后,并不真的把页加载多物理内存里,而是只有在程序运行中,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存里面去。

多级页表

因为操作系统是可以同时运行非常多的进程的,那就意味着页表会非常的庞大。要解决这样的问题,就需要采用一种叫做多级页表的解决方案。多级页表的映射关系如下图所示:

 图3 多级页表映射关系

上述对也不多施行离散分配的方法,虽然解决了对于打野表无线大片连续存储空间的问题,但并未解决用较少的内存空间去存放打页表的问题。能够用较少的内存空间存放页表的唯一方法是,仅把当前需要的一批页表项调入内存,以后在根据需要陆续调入。在采用两级页表结构的情况下,对于正在运行的进程,必须将其外层页表调入内存,而对于页表则只需调入一页或几页。为了表征该页表不在内存中,还应在外层页表项中增设一个状态位。

对于64位系统,则需要四级页表:

  • 全局页目录项PGD
  • 上层页目录项PUD
  • 中间页目录项PMD
  • 页表项PTE

TLB

多级页表对染解决了空间上的问题,但是虚拟地址到物理地址的转换就多了几道转换的工序,这显然就降低了地址转换的速度,也就是带来了时间上的开销。

程序是由局部性的,即在一段时间内,整个程序的执行仅限于程序中的某一部分。相应的,执行所访问的存储空间也局限于某个内存区域。

我们可以利用这一特性,把最常访问的几个页表项存储到访问速度更快的硬件,CPU芯片中,有一个专门存放程序最常访问的页表项的Cache,就是TLB(Translation Lookaside Buffer),通常称为页表缓存、转址旁路缓存、块表等。有了TLB后,那么CPU在寻址是,会先查TLB,如果没找到,才会继续查常规的页表。

段页式内存管理

内存分段和内存分页并不是对立的,它们是可以组合起来在同一个系统中使用的,那么组合起来后,通常称为段页式内存管理。

段页式内存非人力实现的方式:

  • 先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制;
  • 接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页;

这样,地址结构就由段号、段内页号和页内位移三部分组成。

用于段页式地址变换的数据结构是每一个程序一张段表,每个段又建立一张页表,段表中的地址是页表的其实地址,而页表中的地址则为某也的物理页号,如图所示:

 图4 段表和页表实现地址映射

段页式地址变换中要得到物理地址须经过三次内存访问:

  • 第一次访问段表,得到页表起始地址;
  • 第二次访问页表,得到物理页号;
  • 第三次将物理页号与页内位移组合,得到物理地址。
  • 可用软、硬相结合的方法实现段页式地址变换,这样虽然增加了硬件成本和系统开销,但提高了内存的利用率。

总结

为了在多进程环境下,使得进程之间的内存地址不受影响,相互隔离,于是操作系统就为每个进程独立分配一套虚拟地址空间,每个程序只关心自己的虚拟地址就可以,实际上大家的虚拟地址都是一样的,但分布多物理地址内存是不一样的。作为程序,也不用关心物理地址的事情。

每个进程都有自己的虚拟空间,而物理内存只有一个,所以当启用了大量的进程,物理内存必然会很紧张,于是操作系统会通过内存交换技术,把不常使用的内存暂时存放多硬盘,在需要的时候在装载会物理内存。

那既然有了虚拟地址空间,那必然要把虚拟地址映射多物理地址,这个事情通常由操作系统来维护。虚拟地址与物理地址的映射关系,可以有分段和分页的方式,同时两者可以结合。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值