操作系统复习
1. 掌握 CPU 调度算法,熟悉操作系统概念教材 5.3 节中给出的各种调度算法,如先到先服务调度、最短作业优先调度、优先级调度、轮转法调度等,根据给定条件画出 Gantt 图,并计算周转时间和平均周转时间。
2. 掌握进程同步相关概念,并熟练使用同步原语进行编程,见操作系统概念教材第六章。
3. 掌握地址空间的概念,以及与其相关的三个特征(见课程讲义 ppt vm-1);掌握用户态堆内存的使用,并能对判断分析代码中存在的各种内存错误(见课程讲义 ppt vm-1)。
4. 掌握内存溢出(栈溢出)攻击的原理,要求能画出栈溢出攻击的示意图并解释攻击过程;掌握目前常用的三种内存溢出防御方法,包括 cananry、DEP、ASLR(见课程讲义 ppt sec-1)。
5. 掌握虚拟内存管理中的分段与分页机制,及其优缺点(见课程讲义 ppt vm-2);
6. 掌握 TLB 与多级页表(见课程讲义 ppt vm-3),重点了解多级页表相关的计算,比如根据地址位、页面大小、PTE 大小等条件,进行虚拟地址到物理地址的转换。
7. 掌握页码置换算法(见课程讲义 ppt vm-4),要求能根据给定的页面访问序列,给出特定算法的计算过程;掌握 Clock 算法(见课程讲义 ppt vm-4)。
8. 掌握各种磁盘调度算法的原理(见课程讲义 ppt disk-1)。
9. 掌握磁盘阵列 RAID-0,RAID-1,RAID-4,RAID-5 的原理,并分析各种 RAID 在容量、可靠性、吞吐量三个层面的特性(见课程讲义 ppt disk-2)。
10. 掌握文件系统元数据相关属性,要求能根据存储文件的大小来设计元数据中的相关字段(见课程讲义 ppt disk-3)。
11. 掌握文件系统接口,如 open,read,write,在实现时对元数据和文件内容的读写过程(见课程讲义 ppt disk-3)。
12. 了解课堂上讲到的跟操作系统发展史相关的重要人物,并能介绍其主要贡献。
13. 了解操作系统当前的发展现状与未来发展趋势。
- ok掌握 CPU 调度算法,熟悉操作系统概念教材 5.3 节中给出的各种调度算法,如先到先服务调度、最短作业优先调度、优先级调度、轮转法调度等,根据给定条件画出 Gantt 图,并计算周转时间和平均周转时间。
周转时间=实际执行结束时间-到达时间
- the FCFS scheduling algorithm先到先服务算法:
该算法下,按照请求时间顺序依次分配CPU:所以依次是P1、P2、P3:
P1 | P2 | P3 |
0 8 12 13
P1的周转时间:8-0=8
P2的周转时间:12-0.4=11.6
P3的周转时间:13-1=12
t(平均周转时间)=(8+11.6+12)/3=10.53
- the SJF scheduling algorithm最短作业优先算法:
该算法按照下次CPU执行的时间长度进行调度,时间越短越优先。P1先到达以后开始执行,然后t=0.4的时候,P2到达,因为P1还得执行8-0.4=7.6,P2需要执行时间4,所以P2被调度,P3到的时候,P2还得执行3.4,P3得执行1,所以,P3被调度,然后P3执行结束后,P2还得执行3.4,P1还得执行7.6,所以P2被调度,最后P1被调度。
P1 | P3 | P2 |
0 8 9 13
P1的周转时间:8-0=8
P2的周转时间:13-0.4=12.6
P3的周转时间:9-1=8
t(平均周转时间)=(8+12.6+8)/3=9.53
- FCFS:
P1 | P2 | P3 | P4 | P5 |
0 | 2 | 3 11 | 15 | 20 |
SJF:
P2 | P1 | P4 | P5 | P3 |
0 | 1 | 3 | 7 | 12 20 |
non-preemptive priority:
P3 | P5 | P1 | P4 | P2 |
0 8 | 13 | 15 | 19 | 20 |
RR (quantum = 2):
P1 | P2 | P3 | P4 | P5 | P3 | P4 | P5 | P3 | P5 | P3 |
0 2 | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 18 | 20 |
- ok掌握进程同步相关概念,并熟练使用同步原语进行编程,见操作系统概念教材第六章。
- ok掌握地址空间的概念,以及与其相关的三个特征(见课程讲义 ppt vm-1);掌握用户态堆内存的使用,并能对判断分析代码中存在的各种内存错误(见课程讲义 ppt vm-1)。
•
操作系统是内存中的一组例程(一个库)(在本例中从物理地址0开始)。
•
一个正在运行的程序(进程)当前位于物理内存中(在本例中从物理地址64k开始),并使用其余内存。
•实现时间共享的一种方法是运行一个进程一小段时间,让它完全访问所有内存,然后停止它,将其所有状态保存到磁盘(包括所有物理内存),加载其他进程的状态,运行一段时间。
•不幸的是,这种方法有一个大问题:它太慢了,尤其是随着内存的增长。但是保存和恢复寄存器级状态(如PC、通用寄存器等)相对较快。
•因此,我们宁愿在进程之间切换时将其保留在内存中,从而允许操作系统高效地实现时间共享
地址空间
•操作系统需要创建易于使用的物理内存抽象。
•我们将此抽象称为地址空间,它是系统中正在运行的程序的内存视图。
•理解这个内存的基本操作系统抽象是理解内存是如何虚拟化的关键。
目标——透明Transparency
•虚拟内存(VM)系统的一个主要目标是透明性。
•操作系统应该以运行程序不可见的方式实现虚拟内存。
•因此,程序不应该意识到内存是虚拟化的;相反,程序的行为就好像它有自己的私有物理内存。
•在后台,操作系统(和硬件)做所有的工作,在许多不同的作业之间多路复用内存,因此实现了幻觉。
目标——效率Efficiency
•操作系统应该努力使虚拟化尽可能高效。
•就时间 (即不是让程序运行得更慢)和空间(例如不需要为支持虚拟化所需的结构使用太多内存) 而言。
•在实现高效的虚拟化时,OS必须依赖硬件支持,包括TLBs等硬件特性。
目标——保护Protection
•操作系统应该确保进程之间以及操作系统本身不受进程的影响。
•当一个进程执行加载、存储或指令获取时,它不应该能够以任何方式访问或影响任何其他进程或操作系统本身(即其地址空间之外的任何内容)的内存内容。
•因此,保护使我们能够交付流程间隔离的特性。
用户态堆内存的使用,并能对判断分析代码中存在的各种内存错误
调用malloc()使用sizeof()操作符请求适当的空间。
sizeof()被正确地认为是一个操作符,而不是一个函数调用(函数调用将在运行时发生)。
sizeof()认为我们只是在问一个指向整数的指针有多大,而不是我们动态分配了多少内存
例程接受一个参数,一个由malloc()返回的指针。
•您可能会注意到,所分配区域的大小不是由用户传入的,必须由内存分配库本身跟踪
1.Using memory that you have not initialized使用未初始化的内存
j没有初始化
使用的i现在有着未知的值
2.Using memory that you don’t own使用不属于你的内存
可能读取了空指针
读取了0页,因为空指针定义为0
读取了无效的指针的值
给无效的指针指向的内存进行写操作
超出了栈分配的内存的读写
返回的是栈中的数组,不对。
3. Using memory that you haven’t allocated使用未分配的内存
数组越界写操作
4. Faulty heap management错误的堆管理(内存泄漏)
首先是重新分配了内存,然后释放了指针指向的新分配的内存,原来分配的内存泄漏了
指针指向的位置向后移一位,内存泄漏了
释放了栈内存
释放了未分配的内存
释放已经释放过的内存
•
其思想很简单:当在内存中返回空闲块时,仔细查看正在返回的块的地址以及附近的空闲空间块。
•
如果新释放的空间正好位于一个(或两个,如本例中所示)现有空闲块的旁边,则将它们合并到一个更大的空闲块中。
•
因此,合并之后,我们的最终列表应该是这样的:
跟踪分配区域的大小
•您可能已经注意到free(void *ptr)接口不接受大小参数。
•假设给定一个指针,malloc库可以快速确定要释放的内存区域的大小,从而将空间合并回空闲列表中。
•
为了完成这个任务,大多数分配器在头块中存储一些额外的信息,头块保存在内存中,通常就在分发内存块之前。
基本策略
•理想的分配器既快又小碎片。
•不幸的是,由于分配流和空闲请求流可以是任意的(毕竟,它们是由程序员决定的),任何特定的策略在给定错误的输入集时都可能执行得非常糟糕。
•因此,我们不会描述“最佳”方法,而是讨论一些基础知识,并讨论它们的优缺点。
最适合Best Fit
•最佳匹配策略非常简单:首先,搜索空闲列表,找到与请求大小相同或更大的空闲内存块。
•然后,返回这组候选人中最小的一个(也可以称为最小匹配)。
•通过返回接近用户要求的块,best fit试图减少浪费的空间。
•然而,在彻底搜索正确的空闲块时,幼稚的实现会带来严重的性能损失。
最不适合Worst Fit
•最不合适的方法与最合适的正好相反;找到最大的块并返回请求的数量;将剩余的(大)块保留在空闲列表中。
•大多数研究表明,它的性能很差,导致过度的碎片化,但仍然有高的管理费用。
首先满足First Fit
•第一个fit方法只是找到第一个足够大的块,并将请求的数量返回给用户。剩余的空闲空间用于后续请求。
•First fit的优点是速度快——不需要彻底搜索所有空闲空间——但有时会用小对象污染空闲列表的开头。
•因此,分配器如何管理空闲列表的顺序就成了一个问题。一种方法是使用基于地址的排序;通过按照空闲空间的地址对列表进行排序,合并会变得更容易,碎片也会减少。
下一个合适的Next Fit
•不是总是在列表的开头开始第一次匹配搜索,下一次匹配算法会保留一个额外的指针,指向列表中最后一次搜索的位置。
•其思想是将对空闲空间的搜索更均匀地分布在整个列表中,从而避免列表开头的分割。
•这种方法的性能与first fit非常相似,因为再次避免了穷举搜索。
- ok掌握内存溢出(栈溢出)攻击的原理,要求能画出栈溢出攻击的示意图并解释攻击过程;掌握目前常用的三种内存溢出防御方法,包括 cananry、DEP、ASLR(见课程讲义 ppt sec-1)。
bug是实际执行行为可能偏离预期行为的地方。
漏洞是给攻击者带来优势的输入
控制流劫持
指令指针%eip的增益控制
拒绝服务
导致程序崩溃或停止服务客户
信息披露
泄漏私人信息。保存密码
控制流劫持
常见的劫持方法
- 缓冲区溢出
- 格式化字符串攻击
当数据写到为缓冲区分配的空间之外时,就会发生缓冲区溢出。
传统上,我们将exec(" /bin/sh ")的汇编指令注入缓冲区。请参阅“粉碎堆栈的乐趣和利润”的确切字符串或者网上搜索
要生成针对基本缓冲区溢出的漏洞:
- 确定堆栈帧的大小,直到头部的缓冲区
- 大小正确的溢出缓冲区
如果攻击者能够部分或整体地向ANSI C格式函数提供格式字符串,那么就存在格式字符串漏洞。- scut/team teso
防御
减排技术:
金丝雀
防止数据执行/不执行
地址空间布局随机化
cananry
维基百科:“在煤矿中使用金丝雀的历史做法,因为它们比矿工更早受到有毒气体的影响,从而提供了一个生物预警系统。”
终结者金丝雀
4字节:0,CR,LF,-1(低->高)
终止strcpy(), get(),…
随机的金丝雀
加载时选择的4个随机字节
存储在受保护的页面中
需要良好的随机性
序言在addr和当地人之间引入了一个加那利语
尾声在函数返回之前检查金丝雀
错误的Canary =>溢出
检查直到尾声才开始…
func ptr诡计
c++ vtable劫持
异常处理程序劫持
…
性能
每个函数有几个指令
时间:平均几个百分点
大小:可以在安全函数中进行优化(参见MS08-067 *)
部署
重新编译就足够了;没有代码变化
兼容性
perfect-invisible外部
安全保证
不是真的…
DEP
Data Execution Prevention (DEP) / No eXecute (NX)
防止数据执行(DEP) /不执行(NX)
使用NX位将堆栈标记为不可执行
每个内存页都是可写或可执行的。
性能
具有硬件支持:无影响
否则:报告PaX <1%
部署
内核支持(在所有平台上都通用)
模块选择加入(在Windows中不太常见)
兼容性
可以破坏合法程序
即时编译器
解析器
安全保证
注入NX页面的代码永远不会执行
但是代码注入可能没有必要……
按地址覆盖libc函数的返回地址
设置假的返回地址和参数
ret将“调用”libc函数
没有注入代码!
ASLR
地址空间布局随机化(ASLR)
Address Space Layout Randomization
传统的攻击需要精确的地址
基于堆栈的溢出:shell代码的位置
return-to-libc:图书馆地址
问题:程序的内存布局是固定的
堆栈、堆、库等。
解决方案:随机分配每个区域的地址!
性能
非常好——在加载时随机一次
部署
打开内核支持(Windows:每个模块都可以选择,但是存在系统覆盖)
没有必要重新编译
兼容性
对安全应用程序透明(位置独立)
安全保证
x32不行,x64好多了
代码注入可能没有必要……
- ok掌握虚拟内存管理中的分段与分页机制,及其优缺点(见课程讲义 ppt vm-2);
•在虚拟化内存方面,我们将采用与CPU类似的策略,在提供所需虚拟化的同时实现效率和控制。
•效率要求我们使用硬件支持,硬件支持一开始是非常初级的(例如,只有几个寄存器),但会变得相当复杂(例如,TLBs、页表支持,等等,如您将看到的)。
•控制意味着操作系统确保不允许任何应用程序访问任何内存,除了它自己的内存(这里也需要硬件的帮助)。
•在灵活性方面,我们需要VM系统提供更多的支持;具体来说,我们希望程序能够以任何他们想要的方式使用他们的地址空间,从而使系统更容易编程。
•我们将使用的通用技术称为基于硬件的地址转换,或者简称为地址转换。
•通过地址转换,硬件将每次内存访问(例如,指令获取、加载或存储)转换为物理地址,在物理地址中实际放置所需的信息。
•操作系统必须在关键时刻参与进来,以设置硬件,以便进行正确的转换;因此,它必须管理内存,跟踪哪些位置是空闲的,哪些位置正在使用。
•所有这些工作的目标是创造一个美丽的错觉:
有它自己的私有内存,其中存放着它自己的代码和数据。
•虚拟现实的背后隐藏着一个丑陋的物理事实:当CPU(或CPU)在运行一个程序和另一个程序之间切换时,许多程序实际上在同一时间共享内存。
•通过虚拟化,操作系统(在硬件的帮助下)将丑陋的机器现实变成有用、强大且易于使用的抽象。
假设
•我们对内存虚拟化的首次尝试将非常简单。
•具体地说,我们现在假设用户的地址空间必须连续地放在物理内存中。
•为了简单起见,我们还假设地址空间的大小不是太大;具体来说,它小于物理内存的大小。
•我们还将假设每个地址空间的大小完全相同。
•不要担心这些假设听起来不切实际;我们将在运行过程中放松它们,从而实现内存的实际虚拟化。
动态(基于硬件)搬迁
•在20世纪50年代末的第一台分时机器中引入的是一个简单的概念,称为基数和界限。
•具体来说,我们需要在每个CPU中使用两个硬件寄存器:一个称为基寄存器,另一个称为界限寄存器(有时称为极限寄存器)。
•使用base和bounds寄存器,每个程序的编写和编译就像在地址0加载一样。然而,当一个程序开始运行时,操作系统决定在物理内存中的什么位置加载它,并将基本寄存器设置为该值。
基寄存器存的是进程空间的首地址,界限寄存器存的是进程的空间大小,而不是地址。
他两一起提供了保护机制
•将虚拟地址转换为物理地址正是我们所说的地址转换技术。
•因为地址的这种重定位发生在运行时,所以这种技术通常称为动态重定位。
segment
细分:广义基础/
•为了解决这个问题,一个叫做细分的想法诞生了。一个古老的想法,可以追溯到60年代早期。
•想法很简单:与其只有一个基和边界对,为什么不在地址空间的每个逻辑段都有一个基和边界对呢?
•分段允许操作系统将每个段(代码、堆、堆栈)放置在物理内存的不同部分,从而避免用未使用的虚拟地址空间填充物理内存。
•硬件在翻译过程中使用段寄存器。它如何知道到一个段的偏移量,以及地址指向哪个段?
•一种常见的方法是根据虚拟地址的前几位将地址空间分割成段。
•我们示例中的地址空间大小为16KB,可以用214表示
•在我们的示例中,堆栈被重新定位到物理地址28KB,但有一个关键的区别:它向后增长。在物理内存中,它从28KB开始增长到26KB,对应于16KB到14KB的虚拟地址;翻译必须以不同的方式处理。
•除了基值和界值之外,硬件还需要知道段的增长方式(例如,当段的增长方向为正时,设置为1,当为负时设置为0)。
•假设我们希望访问虚拟地址15KB,它应该映射到物理地址27KB。
•我们的二进制虚拟地址是这样的:11 1100 0000 0000(十六进制0x3C00)。硬件使用最上面的两位(11)来指定段,但是我们留下了3KB的偏移量。
•要获得正确的负偏移量,我们必须从3KB中减去最大的段大小:在这个例子中,一个段可以是4KB,因此正确的负偏移量是3KB - 4KB,等于-1KB。
•我们只需将负偏移量(-1KB)添加到基数(28KB),就可以得到正确的物理地址:27KB。边界检查可以通过确保负偏移量的绝对值小于段的大小来计算
操作系统支持
•在上下文切换时,操作系统应该做什么?
•出现的普遍问题是,物理内存很快就充满了空闲空间的小洞,使得分配新内存段或增加现有内存段变得非常困难。我们称这个问题为外部碎片
•这个问题的一个解决方案是通过重新排列现有的段来压缩物理内存。这个解决方案有问题吗?
•一种更简单的方法是使用一个自由列表管理算法,它试图保持大范围的内存可用来分配
paging
我们没有将地址空间分割成三个逻辑段(每个逻辑段大小不同),而是将地址空间分割成固定大小的单元,我们称之为页面。对于分页,物理内存也被划分为若干个页面;我们有时将物理内存中的每个页面称为一个页面帧
优点:
•最重要的改进可能是灵活性:通过完全开发的分页方法,系统将能够有效地支持地址空间的抽象。
•另一个优点是分页提供的自由空间管理的简单性。
页表
•为了记录地址空间的每个虚拟页在物理内存中的位置,操作系统保留一个称为页表的每个进程数据结构。
•页表的主要作用是存储地址空间中每个虚拟页的地址转换,从而让我们知道它们在物理内存中的位置。
•重要的是要记住这个页表是每个进程的数据结构。
•如果要在上面的示例中运行另一个进程,操作系统必须为其管理不同的页表,因为其虚拟页显然映射到不同的物理页
•让我们想象一下,有这么小的地址空间(64字节)的进程正在执行内存访问:
•要转换进程生成的这个虚拟地址,我们必须首先将其分为两个组件:虚拟页码(VPN)和页面内的偏移量。
•对于这个例子,因为进程的虚拟地址空间是64字节,所以我们的虚拟地址(2 6 = 64)需要总共6位。因此,我们的虚拟地址:
•页面大小为64字节地址空间中的16字节;因此,我们需要能够选择4页,地址的前2位就可以做到这一点。
•因此,我们有一个2位虚拟页码(VPN)。
剩下的位告诉我们我们感兴趣的是页面的哪个字节,这里是4位;我们称之为偏移量
我们将每个进程的页表存储在内存中的某个位置。
•
现在让我们假设页面表位于OS管理的物理内存中。
页表中到底有什么?
•页表只是一个数据结构,用于将虚拟地址(或实际上是虚拟页码)映射到物理地址(物理页码)。
•因此,任何数据结构都可以工作。最简单的形式是线性页表,它只是一个数组。
•操作系统通过VPN对数组进行索引,并在索引处查找页表条目(PTE),以找到所需的PFN。
•关于每个PTE的内容,我们有一些不同的位,在某种程度上值得理解。
•有效位通常用来指示特定的转换是否有效;例如,当一个程序开始运行时,它的地址空间的一端是代码和堆,另一端是堆栈。
•所有未使用的中间空间将被标记为无效,如果进程试图访问这些内存,它将向OS生成一个可能终止进程的陷阱。
•因此,有效位对于支持稀疏地址空间至关重要;通过简单地将地址空间中所有未使用的页标记为无效,我们消除了为这些页分配物理帧的需要,从而节省了大量内存。
•我们也可能有保护位,指示页面是否可以被读取、写入或执行。
•同样,以这些位不允许的方式访问页面会给操作系统带来一个陷阱。
当前位表示该页是在物理内存中还是在磁盘上(交换出去)。
•我们将更详细地了解这个研究如何移动的部分地址空间和磁盘为了支持大于物理内存地址空间,并允许页面不积极运行的流程被换出。
•“脏位”也很常见,表示页面进入内存后是否已被修改。
•引用位(即访问位)有时用于跟踪页面是否已被访问,在确定哪些页面受欢迎并因此应保留在内存中时非常有用;这些知识在页面替换过程中至关重要,我们将在后面详细研究这个主题。
•要做到这一点,硬件必须知道页表在进程中的位置。假设单个页表基寄存器包含页表起始位置的物理地址。为了找到PTE的位置,硬件将执行以下功能
•对于每个内存引用(无论是指令获取还是显式加载或存储),分页都要求我们执行一个额外的内存引用,以便首先从页表中获取转换。
那可是一大堆工作啊!额外的内存引用是昂贵的,在这种情况下,可能会使进程慢两倍或更多。
•如果不仔细设计硬件和软件,页表将导致系统运行太慢,并占用太多内存。
•虽然对于我们的内存虚拟化需求,这似乎是一个很好的解决方案,但必须首先克服这两个关键问题。
与以前的方法(如分段)相比,分页有许多优点。
•首先,它不会导致外部碎片,因为分页(按照设计)将内存划分为固定大小的单元。
•其次,它非常灵活,允许稀疏使用虚拟地址空间,简单。
•但是,不加注意地实现分页支持将导致机器速度变慢(需要许多额外的内存访问来访问页表)以及内存浪费(内存被页表而不是有用的应用程序数据填充)。
•使用分页作为支持虚拟内存的核心机制会导致高性能开销。
•通过将地址空间分割成小的、固定大小的单元(例如,分页需要大量的映射信息。
•由于映射信息通常存储在物理内存中,因此分页逻辑上需要对程序生成的每个虚拟地址进行额外的内存查找。
•在每次获取指令或显式加载或存储指令之前,都要去内存中存储翻译信息,这是非常缓慢的。
- ok掌握 TLB 与多级页表(见课程讲义 ppt vm-3),重点了解多级页表相关的计算,比如根据地址位、页面大小、PTE 大小等条件,进行虚拟地址到物理地址的转换。
•为了加速地址转换,我们将添加所谓的翻译后备缓冲区(translation-lookaside buffer,简称TLB)。
•TLB是芯片内存管理单元(MMU)的一部分,它只是流行的虚拟到物理地址转换的硬件缓存。
•在每个虚拟内存引用时,硬件首先检查TLB,看是否保存了所需的转换;如果是这样,则无需查阅页表就可以(快速地)执行转换。
•与所有缓存一样,TLB也是基于以下前提构建的:在常见情况下,在缓存中可以找到翻译(即点击)。
•如果是这样,增加的开销很少,因为TLB靠近处理核心,而且设计得非常快。
•当出现遗漏时,会产生较高的分页成本;必须访问页表才能找到翻译。
•如果这种情况经常发生,程序可能会运行得明显更慢;内存访问非常昂贵,而TLB丢失会导致更多的内存访问。
•因此,我们希望尽可能避免TLB失误。
谁处理TLB失误?
•硬件管理的TLB类似于Intel x86架构。
•硬件必须知道页表在内存中的确切位置(通过页表基寄存器),以及它们的确切格式。
•如果出现遗漏,硬件将“遍历”页表,找到正确的页表条目,并提取所需的翻译。
•软件管理的TLB就像Sun的SPARK V9。
•在TLB未命中时,硬件只会抛出一个异常,并跳转到一个陷阱处理程序(该处理程序在页表中查找翻译),使用特殊的“特权”指令更新TLB。
TLB问题:上下文切换
•使用TLBs,在进程(以及地址空间)之间切换时会出现一些新问题。
•具体来说,TLB包含虚拟到物理的转换,这些转换仅对当前运行的进程有效;这些转换对其他进程没有意义。
•因此,在从一个进程切换到另一个进程时,硬件或操作系统(或两者兼而有)必须小心,确保将要运行的进程不会意外地使用来自以前运行的进程的转换。
•在上面的TLB中,我们显然有一个问题:VPN 10转换成PFN 100 (P1)或PFN 170 (P2),但是硬件无法区分哪个条目是针对哪个进程的。
•因此,为了让TLB正确有效地支持跨多个进程的虚拟化,我们需要做更多的工作。
•一种方法是简单地在上下文切换上刷新TLB,在运行下一个进程之前清空它。刷新操作只是将所有有效位设置为0。
•然而,这是有代价的:每次进程运行时,它必须在触及数据和代码页时导致TLB丢失。
•为了减少这种开销,一些系统添加了硬件支持,以支持跨上下文切换共享TLB。特别是,一些硬件系统在TLB中提供地址空间标识符(ASID)字段。
•使用地址空间标识符,TLB可以同时保存来自不同进程的转换,而不会造成任何混乱。
•在本例中,两个不同进程的两个vpn指向相同的物理页面,其中有两个条目。
•这种情况可能出现,例如,当两个进程共享一个页面(例如代码页)时。
•共享代码页(二进制文件或共享库)非常有用,因为它减少了使用的物理页面的数量,从而减少了内存开销。
•在我们的混合方案中,关键的区别是每段存在一个边界寄存器;每个边界寄存器保存段中最大有效页面的值。
•例如,如果代码段使用它的前3个页面(0,1和2),那么代码段页表将只分配3个条目,边界寄存器将设置为3。
•通过这种方式,与线性页表相比,我们的混合方法实现了显著的内存节省
多级页表
•多层页表背后的基本思想很简单。
•首先,将页表分割成页面大小的单元;然后,如果页表条目的整个页面无效,则根本不要分配该页表的该页。
•要跟踪页表中的一个页是否有效(如果有效,它在内存中的位置),请使用一个名为页目录的新结构。
•因此,页面目录可以用来告诉您页面表的一个页面在哪里,或者整个页面表的页面不包含有效的页面。
•在一个简单的两级表中,页目录每一页包含一个页表条目。
•它由许多页目录条目(PDE)组成。PDE(最低限度)具有有效的位和页帧号(PFN),类似于PTE。
•然而,这个有效位的含义略有不同:如果PDE条目是有效的,这意味着条目指向(通过PFN)的页表中的至少一个页是有效的,即,在该页上由该PDE指向的至少一个PTE中,该PTE中的有效位被设置为1。
多层页表的优点
•首先,多级表只根据使用的地址空间的大小按比例分配页表空间;它是紧凑的,支持稀疏的地址空间。
•其次,如果仔细构造,页表的每个部分都可以整齐地放在一个页面中,从而更容易管理内存;当需要分配或增长页表时,操作系统可以简单地获取下一个空闲页。
倒页表
•在页表的世界中,使用倒排的页表可以节省更多的空间。
•在这里,我们不使用多个页表(系统的每个进程一个),而是使用一个页表,它为系统的每个物理页都有一个条目。
•条目告诉我们哪个进程正在使用这个页面,哪个进程的虚拟页面映射到这个物理页面。
•要找到正确的条目,现在需要通过这个数据结构进行搜索。
•线性扫描代价高昂,因此哈希表通常构建在基本结构之上,以加快查找速度。
•PowerPC就是这种架构的一个例子
•倒排页表说明了我们从一开始所说的:页表只是数据结构。
•你可以用数据结构做很多疯狂的事情,让它们变大或变小,变慢或变快。
•多级和倒排页表只是可以做很多事情的两个例子。
总结
•在内存受限的系统中(像许多旧系统一样),小的结构是有意义的;
•在具有合理的内存量和积极使用大量页面的工作负载的系统中,使用更大的表来加速TLB遗漏可能是正确的选择。
- ok掌握页码置换算法(见课程讲义 ppt vm-4),要求能根据给定的页面访问序列,给出特定算法的计算过程;掌握 Clock 算法(见课程讲义 ppt vm-4)。
交换空间
•交换空间的添加允许操作系统为多个并发运行的进程提供一个大的虚拟内存。
•多程序设计的发明几乎要求交换某些页面的能力,因为早期的机器显然无法同时容纳所有进程所需的所有页面。
•多编程和易用性的结合使我们希望支持使用比物理上可用的更多的内存。
•我们需要做的第一件事是在磁盘上预留一些空间来来回移动页面。
•在操作系统中,我们通常将这种空间称为交换空间。
•因此,我们将简单地假设操作系统可以在页面大小的单元中读写交换空间。
•要做到这一点,操作系统需要记住给定页面的磁盘地址。
知道缓存命中和未命中的次数让我们计算程序的平均内存访问时间(AMAT)。
•其中T M表示访问内存的成本,T D表示访问磁盘的成本。
另一个简单的策略:
随机
•Random具有类似FIFO的属性;它的实现很简单,但是在选择要驱逐哪些块时,它并没有真正地尝试过于智能。
•随机有多幸运完全取决于随机的选择有多幸运(或不幸运)。
•这张图显示了有多少随机点击达到10,000次以上的试验,每个试验使用不同的随机种子。正如您所看到的,有时候(刚好超过40%的时间),Random和optimal一样好,在示例跟踪中实现了6次命中;有时它做得更糟,达到2次或更少。
使用历史:LRU
•不幸的是,任何像FIFO或Random这样简单的策略都可能有一个共同的问题:它可能会踢出一个重要的页面,一个即将被再次引用的页面。
•
正如我们在调度策略中所做的那样,为了提高对未来的猜测,我们再次依赖于过去并使用历史作为指导。
•
例如,如果一个程序在最近访问了一个页面,那么它很可能在不久的将来再次访问该页面。
•页面替换策略可以使用的一种历史信息是频率;如果一个页面被多次访问,可能不应该替换它,因为它显然具有一定的价值。
•页面更常用的属性是访问的近时性;一个页面被访问的时间越近,它再次被访问的可能性就越大。
•这类政策是基于人们所说的局部性原则。
•在必须进行回收时,使用最不频繁的策略(LFU)将替换使用最不频繁的页面。
•类似地,最近最少使用(LRU)策略将替换最近最少使用的页面。
时钟算法
•想象一下,系统的所有页面都排列在一个循环列表中。时钟指针指向某个特定的页面以开始(哪个页面并不重要)。
•当必须进行替换时,操作系统检查当前指向的页P的使用位是1还是0。如果是1,则意味着最近使用了页P,因此不适合替换。
•因此,时钟指针被增加到下一页P + 1, P的使用位被设置为0(清除)。
•该算法继续执行,直到找到一个设置为0的使用位,这意味着这个页面最近没有使用过(或者,在最坏的情况下,所有页面都在整个页面集中搜索过,清除了所有的位)。
其他VM政策
•页面替换不是VM子系统采用的唯一策略。
•对于大多数页面,操作系统只是简单地使用请求分页,这意味着当访问页面时,操作系统会“按需”将页面放入内存中。
抖动
•当一组运行进程的内存需求超过可用物理内存时,操作系统应该怎么做?
•在这种情况下,系统将持续分页,这种情况有时称为抖动。
•一些早期的操作系统使用工作集来控制内存中可以容纳多少进程。
•当内存被超额订阅时,一些Linux版本会运行内存不足杀手。
- ok掌握各种磁盘调度算法的原理(见课程讲义 ppt disk-1)。
FCFS Scheduling
SJF
•不像作业调度,每个作业的长度通常是未知的,使用磁盘调度,我们可以很好地猜测“作业”(即磁盘请求)将花费多长时间。
•通过估计请求的寻道和可能的旋转延迟,磁盘调度程序可以知道每个请求将花费多长时间,因此(贪婪地)首先选择需要最少时间来服务的请求。
•因此,磁盘调度器在操作时会尽量遵循SJF (short job first)的原则。
SSTF: Shortest Seek Time First
•SSTF根据跟踪对I/O请求队列进行排序,首先从最近的跟踪中选择要完成的请求。
•例如,假设当前head的位置在inner track之上,并且我们有对扇区21 (middle track)和2 (outer track)的请求,那么我们首先将请求发送到21,等待它完成,然后将请求发送到2
•首先,驱动器的几何形状对主机操作系统不可用;
相反,它看到的是一组块。幸运的是,这个问题很容易解决。操作系统可以简单地实现最近块优先(near -block-first, NBF),而不是SSTF, NBF用最近的块地址安排请求。
•第二个问题更为根本:饥饿。想象一下,在我们上面的例子中,如果有一个稳定的请求流到当前头部所在的内部轨道。然后,纯SSTF方法将完全忽略对任何其他曲目的请求。
SCAN Scheduling
•该算法最初称为SCAN,它只是按照轨道的顺序在磁盘服务请求之间移动。
•让我们把穿过磁盘的一次扫描称为扫描。因此,如果请求的磁道上的某个块已经在磁盘的这次扫描中得到了服务,则不会立即处理该请求,而是排队等待下一次扫描。
•SCAN有许多变体。
F-SCAN
•在队列执行扫描时冻结待服务队列;此操作将在清理期间传入的请求放置到队列中,以便稍后进行服务。
•这样做可以通过延迟对延迟到达的(但更接近的)请求的服务,避免远程请求的匮乏。
C-SCAN
•c扫描是另一种常见的变体,是圆形扫描的缩写。
•像SCAN一样,C-SCAN将磁头从磁盘的一端移动到另一端,在此过程中处理请求。然而,当磁头到达另一端时,它会立即返回到磁盘的开头,而不会在返回过程中为任何请求提供服务。
•这个算法(及其变体)有时被称为电梯算法,因为它的行为就像一部电梯,要么向上走,要么向下走,而不仅仅是根据楼层的距离为楼层提供服务。
•想象一下,如果你从10楼走到1楼,有人在3点上电梯,按了4下,电梯上升到4,因为它比1“更近”,那该有多烦人!
•正如你所见,电梯算法在现实生活中使用时,可以防止电梯上发生打斗。在磁盘中,它只是防止饥饿。
9. ok掌握磁盘阵列 RAID-0,RAID-1,RAID-4,RAID-5 的原理,并分析各种 RAID 在容量、可靠性、吞吐量三个层面的特性(见课程讲义 ppt disk-2)。
•在分析RAID性能时,可以考虑两个不同的性能指标。
•第一个是单请求延迟。理解单个I/O请求对RAID的延迟非常有用,因为它揭示了在单个逻辑I/O操作期间可以存在多少并行性。
•二是RAID的稳态吞吐量,即,多个并发请求的总带宽。稳态带宽至关重要,因此将是我们分析的重点。
•我们假设有两种类型的工作负载:顺序工作负载和随机工作负载。我们假设磁盘可以在顺序工作负载下以S MB/ S的速度传输数据,在随机工作负载下以R MB/ S的速度传输数据。
RAID0:
•现在让我们评估剥离的能力、可靠性和性能。
•从容量的角度来看,它是完美的:给定N个磁盘,条带传输N个磁盘的可用容量。
•从可靠性的角度来看,条带化也很完美,但不好的方面是:任何磁盘故障都会导致数据丢失。
•最后,性能非常好:所有磁盘都被用于服务用户I/O请求,通常是并行的。
•从稳态吞吐量的角度,我们期望得到系统的全带宽。因此,吞吐量等于N(磁盘数量)乘以S (N·S MB/ S)。对于大量随机I/Os,我们可以再次使用所有磁盘,从而得到N·R MB/s
RAID1
•对于镜像系统,我们只需对系统中的每个块复制多个副本;当然,每个副本都应该放在单独的磁盘上。通过这样做,我们可以容忍磁盘故障。
•在典型的镜像系统中,我们假设每个逻辑块都有两个物理副本。
•当从镜像数组中读取一个块时,RAID可以选择:它可以读取任何一个副本。
•在编写块时,不存在这样的选择:RAID必须更新数据的两个副本,以保持可靠性。但是请注意,这些写操作可以并行进行;
raid4
全条带写是RAID-4写入磁盘的最有效方式。
加法校验
减法校验:
raid5
10. ok掌握文件系统元数据相关属性,要求能根据存储文件的大小来设计元数据中的相关字段(见课程讲义 ppt disk-3)。
跟踪每个文件的信息。为了存储这些信息,文件系统通常有一个名为inode的结构。
为了适应inode,我们还需要在磁盘上为它们保留一些空间。让我们将磁盘的这一部分称为inode表,它仅包含磁盘上索引节点的数组。
在每个索引节点是几乎所有你需要的信息关于一个文件:
- 它的类型(例如,常规文件,目录,等等),
- 它的大小,分配的块的数量,保护信息(如谁拥有这个文件,以及谁可以访问它),
- 一些时间信息,包括文件时创建,修改,或上次访问,以及其数据块的信息驻留在磁盘上(例如,某种类型的指针)。
•我们将文件的所有此类信息称为元数据。
•在设计inode时,最重要的决策之一是如何引用数据块的位置。一种简单的方法是在inode中有一个或多个直接指针(磁盘地址);每个指针指向一个属于文件的磁盘块。
•这种方法是有限的:例如,如果您想要一个非常大的文件(例如,大于一个块的大小乘以直接指针的数量),那么您就没有那么幸运了。
•为了支持更大的文件,文件系统设计人员不得不在inode中引入不同的结构。
•一个常见的想法是有一个特殊的指针,称为间接指针。它不是指向包含用户数据的块,而是指向包含更多指针的块,每个指针指向用户数据。
•假设一个inode可能有固定数量的直接指针(例如,12个)和一个间接指针,并且一个块是4KB和4字节的磁盘地址,这就增加了另外1024个指针;文件可以增长为(12 + 1024)·
4 k或4144 kb。
•毫不奇怪,在这种方法中,您可能希望支持更大的文件。为此,只需向inode添加另一个指针:双间接指针。
•双间接块因此增加了额外1024·1024或100万4KB块的文件增长的可能性,换句话说,支持超过4GB大小的文件。
•总的来说,这种不平衡树被称为指向文件块的多级索引方法。
•让我们来看一个有12个直接指针的例子,以及一个单间接块和一个双间接块。
假设块大小为4 KB,指针为4字节,这个结构可以容纳略大于4的文件
GB的大小(即(12 + 1024 + 1024 2)×4 KB)。
•你能算出添加一个三重间接块可以处理多大的文件吗?
•为什么要使用这样一棵不平衡的树?为什么不换一种方法呢?
•
许多研究人员研究了文件系统及其使用方法。一个发现是大多数文件都很小。
•这种不平衡的设计反映了这样的现实;如果大多数文件确实很小,那么针对这种情况进行优化是有意义的。
•因此,使用少量的直接指针(12是一个典型的数字),inode可以直接指向48 KB的数据,对于较大的文件需要一个(或多个)间接块。
•目录基本上只包含(条目名称、索引节点编号)对的列表。
•文件系统将目录视为一种特殊类型的文件。
因此,一个目录在inode表的某个地方有一个inode (inode的类型字段标记为“directory”,而不是“常规文件”)。
•目录中有inode指向的数据块(可能还有间接块);这些数据块位于简单文件系统的数据块区域中。
•文件系统必须跟踪哪些索引节点和数据块是空闲的,哪些是空闲的,以便在分配新文件或目录时可以为其找到空间。
•因此,空闲空间管理对于所有文件系统都很重要。对于这个任务,我们有两个简单的位图。
11. ok掌握文件系统接口,如 open,read,write,在实现时对元数据和文件内容的读写过程(见课程讲义 ppt disk-3)。
•
在这个简单的例子中,让我们首先假设您只想打开一个文件(例如/foo/bar,读取它,然后关闭它)。对于这个简单的示例,我们假设文件的大小只有4KB(即1块)。
•当您发出open("/foo/bar", O RDONLY)调用时,文件系统首先需要遍历路径名以找到文件栏的inode。
•将涉及哪些I/O操作?
•还要注意,open生成的I/O数量与路径名的长度成正比。
•对于大型目录,我们可能需要读取许多数据块才能找到所需的条目。
•是的,在阅读文件时,生活会变得很糟糕;正如您即将发现的,写出一个文件(尤其是创建一个新文件)甚至更糟。
•与读取不同,对文件的写入也可能分配一个块(例如,除非该块正在被覆盖)。
•在写新文件时,每个写操作不仅要把数据写到磁盘上,还必须首先决定分配给文件哪个块,从而相应地更新磁盘的其他结构(例如,数据位图)。
•因此,每个写一个文件逻辑生成五个I / o:一个读数据位图(将更新为新块标记为使用),写一位图(反映其新的状态到磁盘),两个读然后写inode(更新为新的块的位置),最后一个写实际块本身。
•您可以看到创建文件需要多少工作:
在本例中,I/Os遍历路径名,然后最终创建文件。
•一个读inode位图(找到一个免费的inode),一个inode写入位图(标记分配),写一个新的索引节点本身(初始化),一个目录的数据(链接的高层名称文件的inode号),和一个读和写的目录索引节点更新它。如果目录需要增长以适应新条目,则需要额外的I/Os(即,以获取数据位图,以及新的目录块)。所有这些只是为了创建一个文件!
•早期的文件系统因此引入了固定大小的缓存来保存流行的块。然而,这种静态内存分区可能会造成浪费。
•相比之下,现代系统采用动态分区方法。具体来说,许多现代操作系统将虚拟内存页和文件系统页集成到统一的页面缓存中。
•通过这种方式,可以更灵活地跨虚拟内存和文件系统分配内存,具体取决于哪个系统在给定的时间需要更多的内存。
12. 了解课堂上讲到的跟操作系统发展史相关的重要人物,并能介绍其主要贡献。
Ken Thompson肯·汤普森
- Unix的发明人之一
- inode是索引节点的缩写,索引节点是UNIX发明人Ken Thompson给它的历史名称,之所以使用这个名称是因为这些节点最初被安排在一个数组中,并且在访问特定的inode时被编入索引的数组。
- 具体地说,当Ken Thompson被问到如果他重新设计UNIX会有什么不同时,他回答说:
“我会用e拼写creat。”
Dijkstra艾兹格·W·迪科斯彻
[A+07] Nitin Agrawal, William J. Bolosky, John R. Douceur, Jacob R. Lorch
文件系统元数据的五年研究
2007年2月,加州圣何塞,第31-45页
最近对文件系统实际使用情况的出色分析。使用内部的参考书目来跟踪1980年代早期的文件系统分析论文
杰夫·邦维克(Jeff Bonwick)
一个特别的分配器,由uber工程师杰夫·邦维克(Jeff Bonwick)设计的slab分配器。
Slab分配器:对象缓存内核内存分配器"
一篇关于如何为操作系统内核构建分配器的很酷的文章,以及一个关于如何专门化特定公共对象大小的很好的示例。
旁白:伟大的工程师真的是伟大的
Jeff Bonwick(他不仅编写了本文中提到的slab分配器,还领导了一个了不起的文件系统ZFS)这样的工程师是硅谷的中心。在几乎所有伟大的产品或技术背后,都有一个人(或一小群人),他们在才能、能力和奉献方面远远超过普通人。正如Facebook的马克•扎克伯格(Mark Zuckerberg)所言:“在自己的角色上出类拔萃的人,并不仅仅比相当优秀的人好一点点。”它们好100倍。这就是为什么时至今日,一两个人就能创办一家公司,永远改变世界的面貌(想想谷歌、苹果(Apple)或Facebook)。努力工作,你可能也会成为这样一个“100倍”的人。如果做不到,和这样的人一起工作;你一天学到的比大多数人一个月学到的还要多。如果做不到,就感到难过。
莫里斯
1988年11月2日,主流媒体根据《计算机欺诈和滥用法案》(Computer Fraud and Abuse Act)对莫里斯蠕虫病毒(Morris Worm)的关注定罪,这是第一个利用缓冲区溢出的知名程序
[R69]“关于存储碎片和程序分割的说明”
布莱恩Randell
ACM的通信
第12卷第7页,第365-372页,1969年7月
最早讨论碎片化的论文之一
•
当你在生活中有两种合理但不同的方法时,你应该经常检查这两种方法的结合,看看你是否能同时得到这两种方法的好处。我们称这种组合为混合。
•
Multics的创建者(特别是Jack Dennis)在构建Multics虚拟内存系统时偶然想到了这个想法。
13. 了解操作系统当前的发展现状与未来发展趋势。
在现代计算机系统中,如何使快速、节省空间、可伸缩的能够很好地适应各种工作负载的内存分配器仍然是一个持续的挑战
考的不好不坏,希望分数能有所提升,还有两门,期末结束之后就尘埃落定路途明确,开始爬下一座山,没事,不管结果如何,都可以的,加油陈雅文