概述
存储器分为内存和外存。主存就是内存,外存也称为辅存,就是磁盘,磁带机等外部设备。主存是和CPU通过总线相连的,CPU执行指令需要从内存(即主存,后文同理)从中访问指令和数据。内存被分为了两部分,一部分是系统区(内核子程序占用),一部分是用户区(用户程序、数据)。通常主存的低地址段分给操作系统,高地址段分给用户。我们今天的问题是,操作系统磁盘中的数据怎么装载进内存中呢?
对于系统区,当机器开机时,系统上电,通过硬件会到系统的BIOS中,BIOS中会调用一段程序,检测硬件有无错误,同时到磁盘的一个固定的位置,把操作系统的一些关键的内核代码(仅仅是部分代码)读入到内存中的系统区;对于用户区,怎么样分配?怎么回收?怎么使得内存利用率最大?内存中同一时刻的进程个数决定操作系统的并发度,那么采取什么样的内存分配策略增大操作系统的并发度?所以,本篇博客将对这些问题进行分析。
源程序如何装入内存
一个源程序怎么装载入内存呢?首先得编译变成目标模块,源程序就变成若干个相对0地址开始编址的程序段(逻辑地址),由于每个目标代码都是从0开始编址,所以它们目前并不能执行。下一步要进行链接,把它们合并为一个整体,最后才能装入进入内存之中。
那么这个过程看似简单,怎么样把目标模块链接起来?是编译时进行链接还是在运行时链接?编译时一般不知道指令会放在内存中什么地方(仅知道逻辑地址),可能要靠装入时决定。如果在装入时决定指令要存放在内存某个地址(物理地址),相当于该进程地址被固定了,那么该进程就不能移动了!除此之外,还有对于共享的程序段的问题(比如某个 function 多个进程都共享),那么难道需要每个进程把它都装入一遍吗? no,肯定不需要。所以,程序的装入和链接的过程,和内存分配和管理有紧密的关系。
内存管理的需求
重定位
为什么需要重定位?有两个原因:
1. 多个进程同时在内存中,互相 "挤兑",地址无法事先确定。
2. 活动进程需要换入换出内存,如果地址换入还要保持先前相同的地址,这会是一个极大的限制。
因此,操作系统必须能够以某种方式把程序代码中的内存访问(逻辑地址)转换成实际的物理地址,并反映程序在内存中的位置,也就是具备重定位的能力。
保护
进程必须受到保护,避免其他进程非法读写自己的内存单元。并且,由于重定位,无疑增加保护的难度。因为程序在内存中的位置是不可预测的,一次编译时不可能通过查绝对地址来确保保护。并且,大多数程序语言允许运行时进行地址的动态计算(如数组下标和指针)。因此,必须在运行时检查进程所有的内存访问,确保进程不会越界访问。
注意:内存保护的需要必须由处理器(硬件)来满足,而不是由操作系统(软件)来满足,只是因为操作系统不能预测程序可能产生的所有内存访问;即使可以预测,提前审查每个进程中存在的违法访问也是非常费时的。所以,处理器必须具备保护的能力。
共享
多个进程正在执行同一程序,那就内存中只保存一份,让它们共享吧 :)
逻辑组织
主要体现在分段技术上,后面分析。
物理组织
操作系统存储器物理上分为两级,内存和外存。在这两级存储器间移动信息的任务就是操作系统的责任了,这就是存储管理的本质。(总不让程序员编程时内存不足去换入换出或者覆盖吧 :))
内外存的数据交换
1.用户程序自己控制方式。典型例子是覆盖,早期主存扩充方式,需要程序员清楚了解程序结构,程序调入内存次序,难度较大,且不能实现虚拟存储器。
2.操作系统控制方式
交换技术: 交换技术的目的也是为了缓解内存不够大的矛盾。它利用外存空间(进程交换区),通过对进程实体的整体交换,来满足内存需求,实现多道程序运行。特点:打破了进程运行的驻留性
请求调入方式:程序执行时,如果要访问的程序段或数据段不在内存(此时会触发缺页异常),则操作系统自动地将有关的程序段和数据段调入内存。
预调入:由操作系统预测在不远的将来将会访问到的那些程序段和数据段部分,并在它们访问之前系统选择适当的实际将它们调入内存。