Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2020spring answer #2

Open
wants to merge 4 commits into
base: 2020spring-answer
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions all/04-1-spoc-discussion.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,82 @@ Virtual Address 1e6f(0 001_11 10_011 0_1111):


如果一个用户态进程访问一个合法用户地址,产生内存访问异常后,v9-cpu会把产生异常的pc存在哪里?中断服务例程应该如何设计,可以返回到用户态产生错误的地址再次执行?

## 问答题

1. 覆盖、交换和虚拟存储有何异同,虚拟存储的优势和挑战体现在什么地方?
* 共同点:都是采取层次存储的思路,将暂时不用的内存放到外存中去,以此来缓解内存不足的问题。
* 不同点:
* 粒度不同:覆盖置换的单位是程序模块,交换时整个进程的内存,虚拟存储可以是一个段或者一个页。
* 自动化程度不同:覆盖需要程序自己决定模块划分和置换时机,交换和虚拟存储可由操作系统自动完成。
* 虚拟存储的优势:
* 粒度合适,比较灵活。兼顾了覆盖和交换的好处:可以在较小粒度上置换;自动化程度高,编程简单,受程序本身影响很小。(覆盖的粒度受限于程序模块的大小,对编程技巧要求很高。交换粒度较大,受限于程序所需内存。尤其页式虚拟存储,几乎不受程序影响,一般情况下,只要置换算法合适,表现稳定、高效)
* 页式虚拟存储还可以同时解决内存外碎片。提高空间利用率。
* 虚拟存储的挑战
* 依赖于置换算法的性能。
* 相比于覆盖和交换,需要比较高的硬件支持。
* 较小的粒度在面临大规模的置换时会发生多次较小规模置换,降低效率。典型情况是程序第一次执行时的大量page fault,可配合预取技术缓解这一问题。

1. 什么是局部性原理?为何很多程序具有局部性?局部性原理总是正确的吗?为何局部性原理为虚拟存储提供了性能的理论保证?

局部性原理:程序执行过程中的一个较短时间内,所执行的指令地址和指令的操作数地址,分别局限于一定区域(合理即可)。分为时间、空间、分支局部性。

指令的顺序执行、数组的连续存放、循环的多次重复是局部性原理的主要成因。

局部性原理适用于常见的、出现较多的情况,不一定总是正确的。常见反例:视频等流式数据。

局部性原理表明,在段/页大小不十分小时,程序的短期访问会集中于某一个或者某几个段/页中,缺段/页不会频繁的发生,也就不会频繁访问外存,降低效率。局部性原理为小粒度置换的性能提供了理论保证。

1. 页访问异常的成因有那些?试描述缺页异常的执行流程。

成因:内存不足被换出;尚未调入内存(第一次访问)

流程:略。视频中有详细介绍,合理即可。

1. 一条load指令,最多导致多少次页访问异常?不必纠结于答案,尝试考虑较多情况。

指令和数据都可能缺页。

页表缺页:MMU会首先查找页表,如果页表缺页也会出发缺页异常。这种情况下如果采用多级页表,每一级都可能发生缺页。

不对齐访存:如果访存地址不对齐,可能导致访问两个页面。此时除了最高层页表,其余各层页表可能全部再次缺页。

1. 如果在缺页中断服务例程执行时,再次出现缺页异常,这时计算机系统(软件或硬件)会如何处理?这种情况可能出现吗?

参考

* https://piazza.com/class/i5j09fnsl7k5x0?cid=1292

* https://piazza.com/class/i5j09fnsl7k5x0?cid=761

* https://en.wikipedia.org/wiki/Double_fault

* https://en.wikipedia.org/wiki/Triple_fault

参考回答:

(x86)硬件是允许 PFH 执行过程中又产生 PF 的。如果发生了这种情况,也就是发生了一次异常嵌套,处理器不会管你的异常是不是嵌套的,它只会按照标准异常处理流程,跳到处理例程。

异常嵌套在前若干次(根据内核栈的大小,几百到几千次)都是标准的“跳到 IDT 指向的 handler、push eip/cs、读 TSS 换栈”的异常处理流程。每次嵌套,硬件和软件都会压栈,所以若干次嵌套之后会栈溢出(i.e. 到没有页表映射的地址)。这时继续嵌套,CPU 在尝试跳转到 IDT 指向的 handler 过程中发现不能压栈,因为栈溢出了,压栈会导致 page fault。也就是说,“尝试跳转到 page fault handler 的时候又触发了一个 page fault”,根据 sdm3 表 6-5 这种情况导致 CPU 触发一个 double fault 异常。触发 double fault 异常之后,处理器会按照标准流程尝试跳转到 handler 去处理这个 double fault(注意 double fault 和其他异常一样就是一个普通的异常)。但是栈已经溢出了,所以在跳转到 double fault 的时候再次发生了 page fault(硬件无法压栈 eip 等)。按照表 6-5,这种情况下处理器进入 shutdown mode。

使用 qemu 的时候,qemu 默认检测到 cpu shutdown 之后会自动 reboot(可用参数 -no-reboot 修改这个行为)。

可以看到,处理流程很复杂。因此,如果出现了嵌套 page fault 的情况,你应当认为你的 OS 设计/编码出错。换言之,一个 sensible 的 OS 设计要避免嵌套 page fault 的情况,例如永远不换出 PFH。

注意给学生强调,double fault 不是说【执行】fault handler 的时候触发了 fault,而是【硬件跳转到】fault handler 的时候触发了 fault。并且 double fault 和 #DE #GP 一样就是一个异常。triple fault 才是不可恢复的重大错误,会导致关机。


1. 在页面被换出后,如何记录被换出后算在的磁盘位置?以linux系统交换区为例进行思考。

对于一个页表项,在页面驻留位被置0后,其余位随之失去意义,这是可以在这些位置中存放关于磁盘位置的信息(如交换区区号,交换区内的页槽索引等)

1. 是否所有的页面被换出后都要置于交换区?

不是。例如该数据位代码段,没有经过修改,可以直接刷掉,下次访问时再次从磁盘读取。

2. 发生page fault时,sepc、stval、scause 分别储存了那些内容?
* sepc:发生异常时正在执行的指令地址

* stval:发生异常时访问的地址

* scause:发生异常的种类,缺页异常分为:instruction page fault, load page fault, store/AMO page fault (PS:amo指令表示原子操作)
126 changes: 126 additions & 0 deletions all/04-2-spoc-discussion.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,129 @@
- [LIRS journal paper](http://www.ece.eng.wayne.edu/~sjiang/pubs/papers/jiang05_LIRS.pdf)
- [LIRS-replacement ppt1](http://dragonstar.ict.ac.cn/course_09/XD_Zhang/(6)-LIRS-replacement.pdf)
- [LIRS-replacement ppt2](http://www.ece.eng.wayne.edu/~sjiang/Projects/LIRS/sig02.ppt)

## 问答题

1. [基础] 全局和局部置换算法有何不同?分别有哪些算法?

2. [基础] 简单描述OPT、FIFO、LRU、Clock、LFU的工作过程和特点

3. [进阶,开放,推荐]考虑置换算法的收益和开销,综合评判在何种情境下使用何种算法比较合适呢?

4. [基础] Clock和LFU算法存在那些问题,如何改进?

5. [进阶-,开放] Clock算法仅仅能够记录近期是否访问过这一信息,对于访问的频度几乎没有记录,如何改进这一点?(这里针对恢复计数的LFU算法也可以提出类似问题)

6. [基础] LRU算法的缺页率是否总是优于FIFO算法呢?为何?

7. [基础] 描述belady现象。[进阶] 哪些算法有belady现象?[困难]思考belady现象的成因,尝试给出说明OPT和LRU等没有belady现象的思路(仅仅是思路,大体有一个方向即可)。

8. [进阶] 使用自映射有何优势?有何代价?

9. [进阶] 考虑在 32 位 x86 下使用页表自映射。为了方便用三元组 (a, b, c) 表示虚拟/物理地址 ((a<<22) +(b<<12) + c),其中 0<=a,b<1024,0<=c<4096。假设页目录的物理地址是 (A, B, 0),并且页目录的第 e 项 (0<=e<1024) 是自映射项,就是说在 (A, B, 4*e) 页目录项指向页目录自己的物理地址 (A, B, 0).

9.1 页目录的第 i 项的物理地址是多少?通过自映射访问的虚拟地址是多少?

9.2 用一句赋值语句,取消虚拟地址 (x,y,0) 所在页的地址映射。那重新映射到物理地址 (C,D,0)呢(用 flags 表示页表项的诸标志位)?

9.3 页表项指向的页的物理地址可以直接从页表项的内容读出。但采用自映射后,为什么把页表项的地址(最后两位清零)左移10位,就得到了页表项指向的页的虚拟地址了?

1. [基础]并发进程数和CPU利用率有何关系?为什么会是这样?

关系:当进程数较少时,随着进程数的增加,CPU利用率提高,当进程数到达一定数量后,CPU利用率不再提高,且如果进程数继续增加,CPU利用率迅速下降。

原因:

* 程序的操作一般分为CPU密集型操作和IO密集型操作,且这两种操作交替进行。

* 当进程数量较少时,如果所有的进程都进入IO阶段,CPU就只能等待,利用率较低。

* 当进程数量足够多,能够使得CPU不存在等待状态时,CPU利用率基本保持不变。

* 当进程数过多时,各个进程的工作集数量过多,导致出现了大量缺页,CPU忙于处理缺页,导致CP降低。

1. [进阶-,开放] 如何理解计算机整体处于均衡的繁忙状态?请从负载均衡和各个存储层次两个方面考虑。

负载均衡:指CPU密集的操作和IO密集的操作基本均衡,一方面,CPU不会等待IO而空闲,另一方面,IO不会等待CPU的缺页处理等而阻塞。

各层次存储之间的均衡:指通过选取合适的替换算法,使得越"热"的数据位于层次存储的越上级,使得访问越下层存储的概率快速下降(也就是各层的miss率都比较小)。达成越底层访问次数越少(指数递减),但是读取量越大(也应该是指数递增)的情况。

1. [基础]什么是工作集?什么是常驻集?简单描述工作集算法的工作过程。

工作集:(一段时间内)进程的属性。指某进程在过去一段时间内访问的所有页面构成的集合。

常驻集:与置换算法相关。指当前驻留内存的页面集合。

工作过程:目标是使得常驻集尽可能接近工作集。做法是淘汰前若干次访存没有访问的页面。具体过程如下:

* 维护一个数量固定访存队列(访存窗口)

* 访存时,如果常驻集中的页面在访存窗口中没有访问,将其换出常驻集

* 缺页时,将新页直接加入常驻集

1. [基础]简单描述缺页率算法的思路。

基本思路:通过控制缺页率来控制常驻集的大小。

做法:规定一个期望的缺页率和时间窗口。从过两次缺页的时间间隔来估计缺页率。如果缺页率过高,则缺页时直接将新页面加入常驻集。如果过低,则将时间窗口内没有访问的页面换出常驻集。

1. [基础]什么是抖动?如何减少抖动的出现?

概念:指进程数量过多而各个内存常驻集不足,从而内存常驻页面频繁在各个进程工作集之间切换,进而导致大量缺页的现象。

方法:进行负载控制,选择合适的进程数量。

1. [进阶-]局部置换算法和全局置换算法有何主要区别?局部置换算法是否能作为全局置换算法来使用效果如何?为什么?

区别:局部置换算法页面数量固定,会尽可能利用全部页面。全局算法针对每个进程页面内数量不固定,会主动去除不常用的页面。

效果:效果不好。局部算法不会针对不同进程的特征做出自适应调整。如果不做改动,每一个进程都会近乎耗尽整个物理内存,从而导致严重进程切换后的大量缺页,而事先估计一个进程工作集的大小是困难的。

1. [基础]什么是缓存污染?它和内存抖动有何异同?


缓存污染概念:将不常用的数据放入缓存,挤出常用数据,从而导致缓存利用率降低的现象。

相同:都存在由于新数据写入而使得原有常用数据被换出的现象。

不同:成因和解决办法不相似,内存抖动无法通过有效的置换策略避免,应该进行负载控制。而缓存污染是由于换出页面选择不合理导致的。

1. [基础]面向缓存的置换算法是为了解决什么问题?解决的基本思路为何?

传统的页面置换算法如LRU在IObuffer的情境下不能够适应常见的访问模式,会出现缓存污染的现象。这些访问模式包括:顺序访问、循环访问、时间密集访问、概率访问。

视频中提到的算法主要是对LRU进行了改进,通过利用访问频度的信息,更加合理的区分页面的热度,使得换出真正不常用的页面。

1. [基础]简述FBR的思路和做法?(针对LRU-K也可以出类似题目)

思路:LRU + LFU,在小尺度内使用LRU,避免密集访问导致的LFU计数暴增。大尺度上使用LFU,利用频度信息。

做法:把LRU栈分为三段:新区域、中间区域、旧区域。增加引用计数,新区域被访问计数不增加,淘汰是淘汰旧区域引用计数最少的。

1. [基础]2Q算法针对LRU-K做了那些修改?

K = 2

LRU-K的访问历史队列只保存Buffer Cache的元数据信息;2Q的访问历史队列不仅仅是保存元数据(如访问时间等),也保存Buffer Cache本身了,所以,它与缓冲区都存放了BUFFER,使用FIFO策略;

LRU-K的缓冲区采用优先级队列策略,而2Q的缓冲区简化为LRU队列策略


1. 补充:LRU-K等的特点和算法

我除了看论文,也参考了 http://www.itgo.me/a/x2374856251384962283/Database- 我觉得对LRU-K, 2Q, LIRS的理解还还不错

https://cloud.tencent.com/developer/article/1005742 对LIRS描述比较细

特点:

K指的是最后第K次访问的距离,也就是倒数第K次访问时和最近一次访问的时间差。LRU-K算法主要是对比最后第K次的访问距离,访问距离越大则代表每次的访问间隔越长,因此更容易被替换出cache。

算法简要描述:

1. 数据第一次被访问,加入到访问历史列表;
2. 如果数据在访问历史列表里后没有达到K次访问,则按照一定规则(FIFO,LRU)淘汰;
3. 当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列删除,将数据移到缓存队列中,并缓存此数据,缓存队列重新按照时间排序;
4. 缓存数据队列中被再次访问后,重新排序;
5. 需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即:淘汰“倒数第K次访问离现在最久”的数据。