3.2 一种储存器抽象:地址空间
物理地址暴露给用户有两个问题:
1.容易被用户寻址破坏操作系统 2.同时运行多个程序很困难
3.2.1 地址空间的概念
为了保护和重定位
每个进程都有属于自己的地址空间。
relocation 重定位:把每个进程的地址空间映射到物理内存的不同部分。
利用基址寄存器(起始物理位置)和界限寄存器(地址大小)
缺点就是每次都要用加法计算真实物理地址。
3.2.2 交换技术
两种处理内存超载技术的通用方法:交换和虚拟内存。
把内存看成一整块,每次一个新的进程索取的内存都插入到最下面的空闲区中,并且可以为每个内存的增长预留空间。
3.2.3 空闲内存管理
跟踪内存使用情况:
1.使用位图
把内存分成大小相同的模块(分配单元),每块对应位图中的一个字符,1占用0空闲(相反也可)
2.使用链表
链表中四个区域分别记载:P(进程)/H(空闲)+开始位置+长度+指针
插入方式:
1.first fit 首次适配:第一个能容纳下新模块的空闲区直接分配,余下的地方变成空闲区
2.下次适配:每次找到空闲区时记录位置,下次寻找直接从上次的地方开始找
3.best fit 最佳适配:找出最接近实际需要的空闲区
4.最差适配:总是分配最大的空闲区
5.快速适配:常用大小的空闲区维护一个单独的链表
3.3 虚拟内存
virtual memory 虚拟内存:允许进程部分转入内存就可以执行的技术。
每个程序拥有自己的空间,这个空间被分割成许多块,每块称作一页或页面。
程序引用到不在物理内存的地址空间时,从磁盘中调用内容进入物理内存。
3.3.1 分页
paging 分页:大部分虚拟内存系统中都使用的一种技术,将虚拟地址空间按照固定大小划分成页面的若干单元。
输入是虚拟地址=虚拟页号+偏移量,输出是物理地址=物理页号+偏移量。
缺页中断:访问的页面没有被映射的情况。
3.3.2 页表
行是虚拟内存的页数,高速缓存禁止位+访问位+修改位+保护位+“在不在”位+页框号。
最重要的是页框号,是物理页号,保护位是储存访问类型,修改位记录是否修改过值即最终是否要重新写回到磁盘中,访问位判定被淘汰的页面。
3.3.3 加速分页过程
1.转换检测缓冲区:一个硬件设备,TLB加速分页,在虚拟地址获得时同时进入TLB和页表,如果TLB中有就直接访问TLB,没有就直接访问页表并更新TLB,防止短时间内同页访问发生。
2.软件TLB管理:TLB访问失效时需要更新,将更新其交给操作系统而不是自己的MMU去找,用几条指令完成更新。
软失效:内容在内存里而不在TLB里;
硬失效:也不在内存中,要等硬盘存入。
3.4 页面置换算法
3.4.1 最优(OPT)
找到接下来最晚被访问的页面换掉。
缺点:根本实现不了。
3.4.2 最近未使用
R和W记录是否被读和写过,并定期清空R。于是R相当于近期是否被访问,W相当于内容是否被修改。
第0类:近期没有被访问,没有被修改
第1类:近期没有被访问,已被修改
第2类:近期被访问,没有被修改
第3类:近期被访问,已被修改
3.4.3 先进先出(FIFO)
先淘汰最先进入页表的页面。
缺点:但是最先进入列表的也可能最常访问。
3.4.4 第二次机会
改进FIFO:检车最老页面的R位,如果R位是0就换掉,如果是1就置零然后放入表尾,再找新的表头看是否可以替换。
就是找一个最近没被访问过的页面,如果都被访问过了就是替换最初表头。
3.4.5 时钟
改进第二次机会中需要移动页面到表尾:把链表变成环形链表,如果R=0替换,R=1就将R置0并指针向下移。
3.4.6 最近最少使用(LRU)
前面几条指令中频繁使用的可能接下来也频繁使用。
缺点:实现困难,比如需要遍历整个链表。
3.4.7 软件模拟LRU
老化算法:记录一串01数字,每次将数字右移一位,然后最左位添加R。
3.5.3 页面大小
internal fragmentation 内部碎片:写的内容不一定能占满页面,被浪费掉的空间就是内部碎片。
3.7 分段
目前说的虚拟地址都是一维的,但是很多时候需要多个独立的虚拟地址。
段:每个段从0到最大的线性地址空间序列构成,储存数据和程序,每个段长度可以是0到最大值的任意一个值,段的长度也是可以动态变化的。程序访问必须用段号+段内地址。
分段有助于多个进程之间共享过程和数据。可以把一些内容放在一个单独的段由各个进程共享,不需要在每个进程的地址空间中保存一遍。
分页和分段的区别:
需要程序员
考察点 | 分页 | 分段 |
---|---|---|
需要程序员了解吗 | 不需要 | 需要 |
需要多少线性地址空间 | 1 | 许多 |
共享方便吗 | 否 | 是 |
为什么发明这种技术 | 为了得到大段的线性地址空间 | 为了使程序和数据可以划分为逻辑上独立的地址空间并有助于共享和保护 |
3.7.1 纯分段的实现
页面是定长的,而段不是。
段的内存分配情况与进程内存的分配差不多,也可以通过内存紧缩合并空闲区。
3.7.2 分段与分页结合:MULTICS
一个段也太大,所以把每个段都看作一个虚拟内存将它分页。用一个段做段表记录所有段的描述符,只要一个段的任何一部分在内存中,这个段就被认为在内存中。
每个段表中的描述符=18位页表的主存地址+9位段长度+1位页面大小+1位是否分页+1位+3位其他位+3位保护位
通过这个地址可以找到每个段的页表。
一个地址由两部分构成:段和段内地址
- 根据段号找到段描述符
- 检查是否在内存中
- 检查页表是否在内存中
- 偏移量加到起始地址上
- 进行操作
3.7.3 分段和分页结合:Intel x86
不想看。