前言:
《现代操作系统》这本书跟着《深入理解计算机系统》这本书之后读,确实让我理解了不少之前在CSAPP之中未能完全明白的一些问题,这一章的内容主要讲述了,分页,分段,页面置换算法,寻址等等细节的实现。让我第一次完全明白了整个操作的过程。
我的github:
我实现的代码全部贴在我的github中,欢迎大家去参观(这一部分内容就没有github的代码了,毕竟是实验室的内容)。
https://github.com/YinWenAtBIT
地址空间:
简介:
一、存储器抽象:地址空间:
1. 暴漏物理地址的问题:
如果程序可以寻址内存的每个字节,他们就可以很容易的破坏操作系统。
这种模型,想要同时运行多个程序是很困难的。
2. 解决多个程序在内存中互不影响的关键:
保护和重定位
3. 定义:
地址空间为程序创造了一种抽象的内存,地址空间是一个进程可以用于寻址内存的一套地址集合
4. 基址寄存器和界限寄存器:
程序的起始物理地址装载到基址寄存器中,程序的长度装到界限寄存器中。但是他们的缺点是每次访问内存都需要进行加法和比较运算,效率相对低。
二、内存超载的解决办法:
1. 交换策略:
把一个进程完整的调入内存,运行一段时间后保存回磁盘。
交换策略会在内存中产生空闲区,通过把所有进程尽可能向下移动,有可能将小的空闲区合成一块,该技术成为内存紧缩。
空闲内存管理可以使用位图或者是链表进行存储管理。分配内存有多重算法,对应的是NP问题,现在常用的算法有:首次适配算法,最佳适配算法,下次适配算法等等。
2. 虚拟内存:
虚拟内存的基本思想是:每个程序拥有自己的地址空间,这个空间被划分成多个块,每一块称作一页或者页面。
3. 分页技术:
现在大部分虚拟内存系统都使用分页技术。虚拟地址通过送到MMU转换成物理地址。
虚拟地址空间按照固定大小划分成页面的若干单元,在物理内存中对应的单元称为页框。
现有系统的页大小一般是512字节到64kb。
4. 虚拟地址寻址:
虚拟地址到物理地址的映射可以概括如下:虚拟地址配划分成虚拟页号(高位部分)和偏移量(低位部分)两部分。
5. 页表结构:
保护位指出一个页允许什么类型的访问,访问位记录了对页的访问和修改。
6. 加速分页过程:
虚拟地址到物理地址的映射必须非常快,如果虚拟地址空间很大,则页表也会很大。
可以使用转换检测缓冲区(TLB)来加速转换。TLB中保存了最近使用的虚拟地址
7. 针对大内存的页表
a. 多级页表
b. 倒排页表
三、页面置换算法 :当发生缺页中断时,操作系统必须在内存中选择一个页面将其换出内存,以便为即将调入的页面腾出空间。如果被调出的页面在内存驻留期间已经修改过,就必须把它写回磁盘。
1. 最优页面置换算法:
此算法是理想状态,不可能实现。它希望把最后才使用的页面置换出去,不过由于不可知每个页面的下一次使用时间,所以是做不到的。
2. 最近未使用页面置换算法:
系统为每个页面设置两个状态位。当页面被访问的时候设置R位,当页面被修改的时候设置M位
置换算法为:当启动一个进程时,该进程的所有的页面两个位都设置为0,R位定期的被清零,以区别最近页面有没有访问。
NRU算法随机地从类编号最小的非空类中挑选一个页面淘汰。
3. 先进先出页面算法
该算法淘汰存在时间最长的页面,不实用。
4. 第二次机会页面置换算法:
检查页面的R位,如果R位为0,则被置换出去,如果R位是1,则将R位清零,并将页面放在链表的尾部,并修改它的装入时间,就想好刚刚装入一样。
第二次会算法就是寻找一个最近的时钟间隔以来没有被访问过的页面,如果所有的页面都被访问过,该算法就简化为纯粹的FIFO算法。
5. 时钟页面置换算法:
把所有的页面都保存在一个类似钟面的环形链表中,一个表针指向最老的页面。
6. 最近最少使用页面置换算法:
当出现缺页中断时,置换最长时间未使用的页面
7. 老化算法:
将页面是否被访问,插入到页面的字节高位上,其他位算数右移,那么在需要置换出页面时,置换出字节最小的页面。老化算法与LRU算法的区别在于,字节长度是有限的,只能记录有限的时钟周期。
8. 工作集页面置换算法:
一个进程当前正使用的页面的集合称为它的工作集。系统跟踪进程的工作集,并想办法让进程在运行前,它的工作集就已经在内存中。
9. 工作集时钟页面置换算法:
这个算法就是前面的工作集与时钟算法的结合。
页面算法总结:
LRU算法是非常优秀的算法,但是只能通过特定的硬件来实现。NFU是一种近似于LRU的算法。它的性能不是非常好,但是可以很有效的实现,因此是一个很好的选择。
总之,最好的两种算法是老化算法和工作集时钟算法。他们分别基于LRU和工作集。四、分页系统设计细节问题:
1. 局部策略与全局策略:
如果一个进程发送了缺页中断,但是置换页面时只考虑换出A已有的页面,则是局部策略,如果在所有页面上寻找页面置换,则是全局策略。
2. 页面大小:
页面过大的时候,会有页面空间没有被利用,造成浪费,这种浪费称为内存碎片。
3. 共享库:
在编译共享库时,用一个特殊的编译选项告诉编译器,不要产生使用绝对地址的指令。相反,只能使用相对地址的指令。例如,总是使用向前或向后转跳n个字节。因此,无论共享库放置在地址空间的什么位置,这种指令都可以正确工作。只使用相对偏移量的代码被称为位置无关代码。
五、 缺页中断处理 :1. 硬件陷入内核,在堆栈中保存程序计数器,大多数机器将当前指令的各种状态信息保存在特殊的CPU寄存器中。
2. 启动一个汇编代码例程保存通用寄存器和其他易失信息,以免操作系统被破坏,这个例程将操作系统作为一个函数来调用。
3. 当操作系统发现一个缺页中断时,尝试发现需要哪个虚拟页面。通常一个硬件寄存器包含了这一信息,如果没有的话,操作系统必须检索程序计数器,取出这条指令,用软件分析这条指令,找到它在缺页中断的时候正在做什么。
4. 知道了发生缺页中断的虚拟地址之后,操作系统检查这个地址是否有效,并检查存取与保护是否一致,如果不一致,就向进程发送一个信号或者杀死该进程。如果地址有效,那么就寻找一个可以置换的页框用来置换。
5. 如果选择的页框发生了写操作,即脏了,那么要先写回磁盘,安排该页写回磁盘,并发生一次上下文切换,挂起缺页中断的进程,让其他进程先运行,直到磁盘传输结束。
6. 页框干净后,操作系统查找所需页面在磁盘上的地址,通过磁盘将其装入。
7. 当磁盘发生中断后,表明该页已经被装入,页表已经更新可以反映它的位置,页框也被标记为正常状态
8. 恢复发生缺页中断指令以前的状态,程序计数器重新指向这条指令。
9. 调度引发缺页中断的进程,操作系统返回调用它的汇编语言例程。
10. 该例程恢复寄存器和其他状态信息,返回到用户空间继续执行,就好像缺页中断没有发生过一样。
六、 分段:1. 定义:
在机器上提供多个互相独立的称为段的地址空间,每个段由一个从0到最大的线性地址序列构成。每个段的长度可以是0到某个允许的最大值之家你的任意值。
2. 分段的优点:
a. 每个段都构成了一个独立的地址空间,所以他们可以独立的增长或减小而不会影响其他的段。
b. 如果每个过程都位于一个独立的段中,并且起始地址是0,那么把单独编译好的过程链接起来的操作就可以得到很大的简化。当组成一个程序的所有过程都被编译和链接好之后,一个对段n中过程的调用就将使用由两个部分组成的地址(n,0)来寻址到字0,即入口点。
c. 如果对于段n的过程进行修改并重新编译,即使新版本的程序比旧的要打,也不需要对其他的过程进行修改。
d. 分段也有助于在几个进程之间共享过程和数据,一个例子就是共享库。
3. 段的使用:
一般编译表的分段内存具有5个独立的段,如下图:
图
一般来说,一个段中不会即包括一个过程又包括一个堆栈,而是只会包含其中一个。
4. 分段和分页的结合:
如果一个段比较大,那么把它整个保存在内存中可能很不方便甚至是不可能的,因此需要对段进行分页。只要那些真正被需要的页面才会被调入内存。
一个段描述符包含了一个段是否在内存中的标志,只要段中任意一部分在内存中,这个段就被认为在内存中,并且它的页表也会在内存中。
总结:这一章的内容非常多,且讲的非常的细致,用心阅读,就能正真正的明白为什么需要使用分页,分段技术。整个过程与计算机的体系结构分不开的。现在理解了,还需要以后不时的复习来加深印象。