操作系统内存最全解析!!!( 三 )


操作系统内存最全解析!!!

文章插图
 
使用基址寄存器和变址寄存器是给每个进程提供私有地址空间的一种非常好的方法,因为每个内存地址在送到内存之前,都会先加上基址寄存器的内容 。在很多实际系统中,对基址寄存器和变址寄存器都会以一定的方式加以保护,使得只有操作系统可以修改它们 。在 CDC 6600 中就提供了对这些寄存器的保护,但在 Intel 8088 中则没有,甚至没有变址寄存器 。但是,Intel 8088 提供了许多基址寄存器,使程序的代码和数据可以被独立的重定位,但是对于超出范围的内存引用没有提供保护 。
所以你可以知道使用基址寄存器和变址寄存器的缺点,在每次访问内存时,都会进行 ADD 和 CMP 运算 。CMP 指令可以执行的很快,但是加法就会相对慢一些,除非使用特殊的加法电路,否则加法因进位传播时间而变慢 。
交换技术如果计算机的物理内存足够大来容纳所有的进程,那么之前提及的方案或多或少是可行的 。但是实际上,所有进程需要的 RAM 总容量要远远高于内存的容量 。在 windows、OS X、或者 linux 系统中,在计算机完成启动(Boot)后,大约有 50 - 100 个进程随之启动 。例如,当一个 Windows 应用程序被安装后,它通常会发出命令,以便在后续系统启动时,将启动一个进程,这个进程除了检查应用程序的更新外不做任何操作 。一个简单的应用程序可能会占用 5 - 10MB 的内存 。其他后台进程会检查电子邮件、网络连接以及许多其他诸如此类的任务 。这一切都会发生在第一个用户启动之前 。如今,像是 Photoshop 这样的重要用户应用程序仅仅需要 500 MB 来启动,但是一旦它们开始处理数据就需要许多 GB 来处理 。从结果上来看,将所有进程始终保持在内存中需要大量内存,如果内存不足,则无法完成 。
所以针对上面内存不足的问题,提出了两种处理方式:最简单的一种方式就是交换(swApping)技术,即把一个进程完整的调入内存,然后再内存中运行一段时间,再把它放回磁盘 。空闲进程会存储在磁盘中,所以这些进程在没有运行时不会占用太多内存 。另外一种策略叫做虚拟内存(virtual memory),虚拟内存技术能够允许应用程序部分的运行在内存中 。下面我们首先先探讨一下交换
交换过程下面是一个交换过程
操作系统内存最全解析!!!

文章插图
 
刚开始的时候,只有进程 A 在内存中,然后从创建进程 B 和进程 C 或者从磁盘中把它们换入内存,然后在图 d 中,A 被换出内存到磁盘中,最后 A 重新进来 。因为图 g 中的进程 A 现在到了不同的位置,所以在装载过程中需要被重新定位,或者在交换程序时通过软件来执行;或者在程序执行期间通过硬件来重定位 。基址寄存器和变址寄存器就适用于这种情况 。
操作系统内存最全解析!!!

文章插图
 
交换在内存创建了多个 空闲区(hole),内存会把所有的空闲区尽可能向下移动合并成为一个大的空闲区 。这项技术称为内存紧缩(memory compaction) 。但是这项技术通常不会使用,因为这项技术回消耗很多 CPU 时间 。例如,在一个 16GB 内存的机器上每 8ns 复制 8 字节,它紧缩全部的内存大约要花费 16s 。
有一个值得注意的问题是,当进程被创建或者换入内存时应该为它分配多大的内存 。如果进程被创建后它的大小是固定的并且不再改变,那么分配策略就比较简单:操作系统会准确的按其需要的大小进行分配 。
但是如果进程的 data segment 能够自动增长,例如,通过动态分配堆中的内存,肯定会出现问题 。这里还是再提一下什么是 data segment 吧 。从逻辑层面操作系统把数据分成不同的段(不同的区域)来存储:
  • 代码段(codesegment/textsegment):
又称文本段,用来存放指令,运行代码的一块内存空间
此空间大小在代码运行前就已经确定
内存空间一般属于只读,某些架构的代码也允许可写
在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等 。
  • 数据段(datasegment):
可读可写
存储初始化的全局变量和初始化的 static 变量
数据段中数据的生存期是随程序持续性(随进程持续性)随进程持续性:进程创建就存在,进程死亡就消失
  • bss段(bsssegment):
可读可写
存储未初始化的全局变量和未初始化的 static 变量


推荐阅读