操作系统原理(五)存储管理

  操作系统区别于系统软件,最大的两个区别:一个是进程管理,一个是存储管理。

操作系统中的程序的内存结构

  一个程序本质上都是由BSS段、data段、text段三个组成的。可以看到一个可执行程序在存储(没有调入内存)时分为代码段数据区未初始化数据区三部分。

  1. BSS段(未初始化数据区):通常用来存放程序中未初始化的全局变量和静态变量的一块内存区域。BSS段属于静态分配,程序结束后静态变量资源由系统自动释放。
  2. 数据段:存放程序中已初始化的全局变量的一块内存区域,数据段也属于静态内存分配。
  3. 代码段:存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量。

  text段和data段在编译时已经分配了空间,而BSS段并不占用可执行文件的大小,它是由链接器来获取内存的。

  bss段(未进行初始化的数据)的内容并不存放在磁盘上的程序文件中。其原因是内核在程序开始运行前将它们设置为0。需要存放在程序文件中的只有正文段和初始化数据段。

  data段(已经初始化的数据)则为数据分配空间,数据保存到目标文件中。

  数据段包含经过初始化的全局变量以及它们的值。BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段的后面。当这个内存进入程序的地址空间后全部清零。包含数据段和BSS段的整个区段此时通常称为数据区。

  可执行程序在运行时又多出两个区域:栈区堆区

  • 栈区:由编译器自动释放,存放函数的参数值、局部变量等。每当一个函数被调用时,该函数的返回类型和一些调用的信息被存放到栈中。然后这个被调用的函数再为他的自动变量和临时变量在栈上分配空间。每调用一个函数一个新的栈就会被使用。栈区是从高地址位向低地址位增长的,是一块连续的内存区域,最大容量是由系统预先定义好的,申请的栈空间超过这个界限时会提示溢出,用户能从栈中获取的空间较小。
  • 堆区:用于动态分配内存,位于BSS和栈中间的地址区域。由程序员申请分配和释放。堆是从低地址位向高地址位增长,采用链式存储结构。频繁的malloc/free造成内存空间的不连续,产生碎片。当申请堆空间时库函数是按照一定的算法搜索可用的足够大的空间。因此堆的效率比栈要低的多。

内存溢出和内存泄漏

  1. 内存溢出

  指程序申请内存时,没有足够的内存供申请者使用。内存溢出就是你要的内存空间超过了系统实际分配给你的空间,此时系统相当于没法满足你的需求,就会报内存溢出的错误。

  内存溢出原因:

  • 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  • 集合类中有对对象的引用,使用完后未清空,使得不能回收;
  • 代码中存在死循环或循环产生过多重复的对象实体;
  • 使用的第三方软件中的BUG
  • 启动参数内存值设定的过小。
  1. 内存泄漏

  内存泄漏是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

  1. 内存泄漏的分类
  • 堆内存泄漏 (Heap leak)。对内存指的是程序运行中根据需要分配通过mallocrealloc new等从堆中分配的一块内存,再是完成后必须通过调用对应的free或者delete删掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生Heap Leak
  • 系统资源泄露(Resource Leak)。主要指程序使用系统分配的资源比如BitmaphandleSOCKET等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。
  • 没有将基类的析构函数定义为虚函数。当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露。

内存管理

  用户对存储器功能的需求主要有:容量足够大速度足够快信息永久保存多道程序并行。但是多道程序并行会带来一些问题,比如如何实现共享代码和数据,节省内存,不允许内存中的程序相互间非法访问等。

  实际的存储体系是三级存储体系:Cache(快,小,贵)、内存(适中)、辅存(慢,大,廉)。

CPU可以直接和Cache内存交换数据。当内存太小不够用时,用辅存来支援内存。暂时不运行的模块换出到辅存上,必要时再换入内存。

地址映射

  存储管理模块有四大功能:

  地址映射说的是:把程序中的地址(虚拟地址/虚地址/逻辑地址)变换成内存的真实地址(实地址/物理地址)的过程。地址映射也称作地址重定位,地址重映射。地址映射的方式有三种:固定地址映射静态地址映射动态地址映射

  • 固定地址映射编程或编译时确定逻辑地址和物理地址映射关系。固定地址映射的特点是:程序加载时必须放在指定的内存区域。缺点是容易产生地址冲突,运行失败。比如大家都喜欢带86的数字。

  • 静态地址映射:程序装入时由操作系统完成逻辑地址到物理地址的映射。静态地址的过程分析:

  每个虚拟地址他对应的物理地址(MA Memory Addr)为:逻辑地址(VA Virtual Addr)加装入基址(BA Base Addr),装入基址是这个程序装在内存里面的起始地址

M A = B A + V A MA = BA + VA MA=BA+VA

静态地址映射的特点是:程序运行之前确定映射关系,程序转入后不能移动,如果移动必须放回原来位置,否者这个程序不能正确运行。还有就是程序要占用连续的内存空间。

  • 动态地址映射:在程序执行过程中把逻辑地址转换为物理地址。例如 : MOV AX, (500]) ;访问500单元时执行地址转换:

M A = B A + V A MA = BA + VA MA=BA+VA

  这里要注意,如果程序有移动,BA可能会有改变,自动计算新的MA

动态地址映射的特点是:程序占用的内存空间可动态变化,要求及时更新基址BA,程序不要求占用连续的内存空间。每段放置基址BA系统应该知道。能够便于多个进程共享代码,共享代码作为独立的一段存放。它的缺点就是需要有硬件支持(MMU:内存管理单元)。

虚拟存储

  虚拟存储要解决的问题有两个:第一、程序过大或过多时,内存不够,不能运行。第二、多个程序并发时地址冲突,不能运行。采用虚拟存储就可以解决以上这两个概念。

虚拟内存是面向用户的虚拟封闭存储空间。这个存储空间是一个线性的存储空间,从0开始一直增加到最大。在32位系统里面,这个容量是4G= 2 32 2^{32} 232Byte,对每个进程来说都有这样的一个封闭空间。虚拟地址和物理地址是分离的,地址无冲突。虚拟内存能够使得大的程序在较小的内存中运行,使得多个程序在较小的内存中运行(能容纳下)。多个程序并发运行时地址不冲突(方便,高效)。能够使得内存利用效率高,无碎片,共享方便。

Linux虚拟地址空间

  为了防止不同进程同一时刻在物理内存中运行时,对物理内存的争夺和践踏,采用了虚拟内存。

  虚拟内存技术使得不同进程在运行过程中,它所看到的是自己独自占有了当前系统的4G内存。所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。 事实上,在每个进程创建加载时,内核只是为进程创建了虚拟内存的布局,具体就是初始化进程控制表中内存相关的链表,实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text,.data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射),等到运行到对应的程序时,才会通过缺页异常,来拷贝数据。还有进程运行过程中,要动态分配内存,比如malloc时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。

  请求分页系统、请求分段系统和请求段页式系统都是针对虚拟内存的,通过请求实现内存与外存的信息置换。

  • 虚拟内存的好处
  1. 扩大地址空间。
  2. 内存保护:每个进程运行在各自的虚拟内存地址空间,互相不能干扰对方。虚存还对特定的内存地址提供写保护,可以防止代码或数据被恶意篡改。
  3. 公平内存分配。采用了虚存之后,每个进程都相当于有同样大小的虚存空间。
  4. 当进程通信时,可采用虚存共享的方式实现。
  5. 当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存。
  6. 虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。在内存中可以保留多个进程,系统并发度提高。
  7. 在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片。
  • 虚拟内存的代价
  1. 虚存的管理需要建立很多数据结构,这些数据结构要占用额外的内存。
  2. 虚拟地址到物理地址的转换,增加了指令的执行时间。
  3. 页面的换入换出需要磁盘I/O,这是很耗时的。
  4. 如果一页中只有一部分数据,会浪费内存。

内存分配

  为程序运行分配足够的内存空间。内存分配需要解决的问题有:

  放置策略:程序调入内存时将其放置在哪个/哪些内存区。
  调入策略:何时把要运行的代码和要访问的数据调入内存。
  淘汰策略:内存空间不够时,迁出( /淘汰)哪些代码或数据。

存储保护

  保证在内存中的多道程序只能在给定的存储区域内活动并互不干扰。要防止越界、防止越权。

  方法是采用界址寄存器:在CPU中设置一对下限寄存器和上限寄存器存放程序在内存中的下限地址和上限地址。程序访问内存时硬件自动将目的地址与下限寄存器和上限寄存器中存放的地址界限比较,判断是否越界。

物理内存管理

分区内存管理

  分区内存管理是将物理内存分配成若干个区域给用户使用。

  根据分区的形式和分区的时机,我们可以分为多种分区方式:单一区存储管理,分区存储管理,分区存储管理又可细分为固定分区和动态分区。

  1. 单一区存储管理也称为不分区存储管理。这种存储管理方式的用户区不分区,完全被一个程序占用,例如DOS。这种分区的优点是:简单,不需复杂硬件支持,适于单用户单任务OS缺点是:程序运行占用整个内存,即使小程序也是如此,内存浪费,利用率低。

  2. 分区存储管理:把用户区内存划分为若干大小不等的分区,供不同程序使用,适合单用户单任务系统。这种分区方式依据分区的时机又可以分为固定分区动态分区

  • 固定分区:把内存固定地划分为若干个大小不等的分区供各个程序使用。每个分区的大小和位置都固定,系统运行期间不再重新划分。此时:操作系统会记录一个分区表,这个分区表记录分区的位置、大小和使用标志。

固定分区的特点是:在程序装入前,内存已被分区,不再改变。每个分区大小不同,适应不同大小的程序。还有一个就是系统需要维护一个分区表。
固定分区的缺点:浪费内存:程序比所在分区小;大程序可能无法运行程序可能比最大分区大。
建议:根据分区表安排程序装入顺序,使每个程序都能找到合适的分区运行。当程序的大小、个数、装入顺序等都固定时,内存使用效率很高。在Windows操作系统中程序的大小、个数、装入顺序等都不固定&#

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值