2017-2018-1 20155306 《信息安全系统设计基础》第11周学习总结
2017-2018-1 20155306 《信息安全系统设计基础》第11周学习总结
教材学习内容总结
第9章 虚拟内存
为了更加有效地管理内存并且少出错,现代系统提供了一种对主存的抽象概念,叫做虚拟内存(VM)。虚拟内存是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的完美交互,它为每个进程提供了一个大的、一致的和私有的地址空间。通过一个很清晰的机制,虚拟内存提供了三个重要的能力:1)它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,它高效地使用了主存。2)它为每个进程提供了一致的地址空间,从而简化了内存管理。3)它保护了每个进程的地址空间不被其他进程破坏。
虚拟内存是中心的。
虚拟内存是强大的。
虚拟内存是危险的。
9.1 物理和虚拟寻址
当CPU执行这条加载指令时,它会生成一个有效物理地址,通过内存总线,把它传递给主存。主存取出从物理地址4处开始的4字节字,并将它返回给CPU,CPU会将它存放在一个寄存器里。现代处理器使用的是一种称为虚拟寻址的寻址形式。
将一个虚拟地址转化为物理地址的任务叫做地址翻译。CPU芯片上叫做内存管理单元的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容是由操作系统管理的。
9.2 地址空间
地址空间是一个非负整数地址的有序集合。
如果地址空间中的整数是连续的,那么我们说它是一个线性地址空间。
一个地址空间的大小是由表示最大地址所需要的位数来描述的。一个系统还有一个物理地址空间,它与系统中物理内存的M个字节相对应。
9.3虚拟内存作为缓存的工具
VM系统通过将虚拟内存分割为称为虚拟页的大小固定的块来处理这个问题。物理内存被分割为物理页大小也为P字节(物理页也称为页帧)。
在任意时刻,虚拟页面的集合都分为三个不相交的子集:
- 未分配的:VM系统还未分配(回或者创建)的页。
- 缓存的:当前缓存在物理内存中的已分配页。
- 未缓存的:没有缓存在物理内存中的已分配页。
9.3.1 DRAM缓存的组织结构
我们使用术语SRAM缓存来表示位于CPU和主存之间的L1、L2和L3高速缓存,并且用术语DRAM缓存来表示虚拟内存系统的缓存,它在主存中缓存虚拟页。
在存储层次结构中,DRAM缓存的位置对它的组织结构有很大的影响。
因为对磁盘的访问时间很长,DRAM缓存总是使用写回,而不是直写。
9.3.2 页表
这些功能是由许多软硬件联合提供的,包括操作系统软件、MMU(内存管理单元)中的地址翻译硬件和一个存放在物理内存中叫做页表的数据结构,页表将虚拟页映射到物理页。
页表就是一个页表条目的数组。
9.3.3 页命中
9.3.4 缺页
在虚拟内存的习惯说法中,DRAM缓存不命中称为缺页。
缺页异常调用内核中的缺页异常处理程序,该程序会选择一个牺牲页。
9.3.5 分配页面
当操作系统分配一个新的虚拟内存页时对我们示例页表的影响。
9.3.6 又是局部性救了我们
尽管在整个运行过程中程序引用的不同页面的总数可能超出物理内存总的大小,但是局部性原则保证了在任意时刻,程序将往往在一个较小的活动页面集合上工作,这个集合叫做工作集或者常驻集合。
如果工作集的大小超出了物理内存的大小,那么程序将产生一种不幸的状态,叫做抖动,这时页面将不断地换进换出。
9.4 虚拟内存作为内存管理的工具
按需页面调度和独立的虚拟地址空间的结合,对系统中内存的使用和管理造成了深远的影响。VM简化了链接和加载、代码和数据共享,以及应用程序的内存分配。
- 简化链接
- 简化加载
- 简化共享
- 简化内存分配
9.5 虚拟内存作为内存保护的工具
任何现代计算机系统必须为操作系统提供手段来控制对内存系统的访问。不应该允许一个用户进程修改它的只读代码段。而且也不应该允许它读或修改任何内核中的代码和数据结构。不应该允许它读或者写其他进程的私有内存,并且不允许它修致任何与其他进程共享的虚拟页面,除非所有的共享者都显式地允许它这么做(通过调用明确的进程间通信系统调用)。
提供独立的地址空间使得分离不同进程的私有内存变得容易。但是,地址翻译机制可以以一种自然的方式扩展到提供更好的访问控制。
9.6 地址翻译
当页面命中时,CPU硬件执行的步骤
- 第1步:处理器生成一个虚拟地址,并把它传送给MMU。
- 第2步:MMU生成PTE地址,并从高速缓存/主存请求得到它。
- 第3步:高速缓存/主存向MMU返回PTE。
- 第4步:MMU构造物理地址并把它传送给高速缓存/主存。
- 第5步:高速缓存/主存返回所请求的数据字给处理器。
页面命中完全是由硬件来处理的,与之不同的是,处理缺页要求硬件和操作系统内核协作完成。
- 第1步到第3步:和图中的第1步到第3步相同。
- 第4步:PTE中的有效位是零,所以MMU触发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序。
- 第5步:缺页处理程序确定出物理内存中的牺牲页,如果这个页面已经被修改了,则把它换出到磁盘。
- 第6步:缺页处理程序页面调入新的页面,并更新内存中的PTE。
- 第7步:缺页处理程序返回到原来的进程,再次执行导致缺页的指令。CPU将引起缺页的虚拟地址重新发送给MMU。因为虚拟页面现在缓回的物理内存中,所以就会命中,在MMU执行了图中的步骤之后,主存就会将所请求字返回给处理器。
9.6.1 结合高速缓存和虚拟内存
在任何既使用虚拟内存又使用SRAM高速缓存的系统中,都存在应该使用虚拟地址还是使用物理地址来访问SRAM高速缓存的问题,但是大多数系统是选择物理寻址的。使用物理寻址,多个进程同时在高速缓存中有存储块和共享来自相同虚拟页面的块成为很简单的事情。而且,高速缓存无需处理保护问题,因为访问权限的检查是地址翻译过程的一部分。
9.6.2 利用TLB加速地址翻译
如果PTE正碰巧缓存在L1中,那么开销就下降到1个或2个周期。然而,许多系统都试图消除即使是这样的开销,它们在MMU中包括了一个关于PTE的小的缓存,称为翻译后备缓冲器。
TLB是一个小的、虚拟寻址的缓存,其中每一行都保存着一个由单个PTE组成的块。TLB通常有高度的相联性。如果TLB有T=2的t次幂个组,那么TLB索引(TLB1)是由VPN的t个最低位组成的,而TLB标记(TLBT)是由VPN中剩余的位组成的。
9.6.3 多级页表
实际上,带多级页表的地址翻译并不比单级页表慢很多。
9.6.4 综合:端到端的地址翻译
为了保证可管理性,我们做出如下假设:
-
内存是按字节寻址的。
-
内存访问是针对1字节的字的。
-
虚拟地址是14位长的。
-
物理地址是12位长的。
-
页面大小是64字节。
-
TLB是四路组相联的,总共有16个条目。
-
L1 d-cache是物理寻址、直接映射的,行大小为4字节,而总共有16个组。
-
TLB。TLB是利用VPN的位进行虚拟寻址的。
-
页表。这个页表是一个单级设计,一共有256个页表条目(PTE)。
-
高速缓存。直接映射的缓存是通过物理地址中的字段来寻址的。
9.7 案例研究:intel core i7/linux内存系统
处理器封装包括四个核、一个大的所有核共享的L3高速缓存,以及一个DDR3内存控制器。
9.7.1 core i7地址翻译
PTE有三个权限位,控制对页的访问。R/W位确定页的内容是可以读写的还是只读的。U/S位确定是否能够在用户模式中访问该页,从而保护操作系统内核中的代码和数据不被用户程序访问。XD(禁止执行)位是在64位系统中引入的,可以用来禁止从某些内存页取指令。
每次访问一个页时,MMU都会设置A位,称为引用位。内核可以用这个引用位来实现它的页替换算法。每次对一个页进行了写之后,MMU都会设置D位,又称修改位或脏位。修改位告诉内核在复制替换页之前是否必须写回牺牲页。
9.7.2 linux虚拟内存系统
一个虚拟内存系统要求硬件和内核软件之间的紧密协作。
内核虚拟内存包含内核中的代码和数据结构。
内存虚拟内存的其他区域包含每个进程都不相同的数据。
1.Linux虚拟内存区域
Linux将虚拟内存组织成一些区域的集合。一个区域就是已经存在着的(已分配的)虚拟内存的连续片,这些页是以某种方式相关联的。
2.Linux缺页异常处理
- 虚拟地址A是合法的吗?
- 试图进行的内存访问是合法的吗?
- 选择一个牺牲页面,如果这个牺牲页面被修改过,那么就将它交换出去,换入新的页面并更新页表。
9.8 内存映射
Linux通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射。
虚拟内存区域可以映射到两种类型的对象的一种:
- Linux文件系统中的普通文件。
- 匿名文件。映射到匿名文件的区域中的页面有时也叫做请求二进制零的页(demand-zero page)。
无论在哪种情况下,一旦一个虚拟页面被初始化了, 它就在一个由内核维护的专门的交换文件(swap file)之间换来换去。交换文件也叫做交换空间(swap space)或者交换区域(swap area)。
9.8.1 再看共享对象
一个对象可以被映射到虚拟存储的一个区域,要么作为共享对象,要么作为私有对象。
一个映射到共享对象的虚拟内存区域叫做共享区域。类似地,也有私有区域。
私有对象使用一种叫做写时拷贝(copy-on-write)的巧妙技术被映射到虚拟内存中。
9.8.2 再看fork函数
当fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID。
9.8.3 再看execve函数
假设运行在当前进程中的程序执行了如下的调用:
Execve("a.out",NULL,NULL) ;
加载并运行a.out需要以下几个步骤:
- 删除已存在的用户区域。
- 映射私有区域。
- 映射共享区域。
- 设置程序计数器(PC)。
9.8.4 使用mmap函数的用户级内存映射
连续的对象片大小为length字节,从距文件开始处偏移量为offset字节的地方开始。start地址仅仅是一个暗示,通常被定义为NULL。
参数prot包含描述新映射的虚拟内存区域的访问权限位:
- PROT_EXEC:由可以被CPU执行的指令组成
- PROT_READ:可读
- PROT_WRITE:可写
- PROT_NONE:不能被访问
9.9 动态内存分配
动态内存分配器维护着一个进程的虚拟内存区域,称为堆。
分配器将堆视为一组不同大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。
分配器有两种基本风格:
- 显示分配器
- 隐式分配器
9.9.1 malloc和free函数
C标准库提供了一个称为malloc程序包的显式分配器。程序通过调用malloc函数来从堆中分配块。
动态内存分配器,例如malloc,可以通过使用mmap和munmap函数,显式地分配和释放堆内存,或者还可以使用sbrk函数。
9.9.2 为什么要使用动态内存分配
9.9.3 分配器的要求和目标
显式分配器必须在一些相当严格的约束条件下工作:
-
处理任意请求序列
-
立即响应请求
-
只使用堆
-
对齐块(对齐要求)
-
不修改已分配的块
-
目标1:最大化吞吐率
-
目标2:最大化内存利用率
9.9.4 碎片
造成堆利用率很低的主要原因是一种称为碎片的现象,有两种形式的碎片:内部碎片和外部碎片。
- 内部碎片是在一个已分配块比有效载荷大时发生的。
- 外部碎片是当空闲内存合计起来足够满足一个分配要求,但是没有一个单独的空闲块足够大可以来处理这个请求时发生的。
9.9.5 实现问题
一个实际的分配器要在吞吐率和利用率之间把握好平衡,就必须考虑以下几个问题:
- 空闲块组织
- 放置
- 分割
- 合并
9.9.6 隐式空闲链表
隐式空闲链表是因为空闲块是通过头部的大小字段隐含地连接着的。
9.9.7 放置已分配的块
当一个应用请求一个k字节的块时,分配器搜索空闲链表,查找一个足够大可以放置所请求块的空闲块。分配器执行这种搜索的方式是由放置策略确定的。
首次分配从头开始搜索空闲链表,选择第一个合适的空闲块。下一次适配和首次适配很相似,只不过不是从链表的起始处开始每次搜索,而是从上一次查询结束的地方开始。最佳适配检查每个空闲块,选择适合所请求大小的最小空闲块。
9.9.8 分割空闲块
9.9.9 获取额外的堆内存
9.9.10 合并空闲块
这些邻接的空闲块可能引起的一种现象,叫做假碎片,就是有许多可用的空闲块被切割成小的、无法使用的空闲块。
为了解决假碎片问题,任何实际的分配器都必须合并相邻的空闲块,这个过程称为合并。分配器可以选择立即合并或选择推迟合并。也就是等到某个稍晚的时候再合并空闲块。
9.9.11 带边界标记的合并
想要释放的块称为当前块。
边界标记
考虑当分配器释放当前块时所有可能存在的情况:
- 前面的块和后面的块都是已分配的。
- 前面的块是已分配的,后面的块是空闲的
- 前面的块是空闲的,而后面的块是已分配的。
- 前面的和后面的块都是空闲的。
9.9.12 综合:实现一个简单的分配器
- 通用分配器设计
- 操作空闲链表的基本常数和宏
- 创建初始空闲链表
- 释放和合并块
- 分配块
9.9.13 显式空闲链表
一种方法是用后进先出(LIFO)的顺序维护链表,将新释放的块放置在链表的开始处。
另一种方法是按照地址顺序来维护链表,其中链表中每个块的地址都小于它后继的地址。
9.9.14 分离的空闲链表
1.简单分离存储
- 优点:分配和释放块都是很快的常数时间操作。
- 缺点:简单分离存储很容易造成内部和外部碎片。
2.分离适配
3.伙伴系统
- 优点:快速搜索和快速合并。
- 缺点:可能导致显著的内部碎片。
9.10 垃圾收集
垃圾收集器是一种动态存储分配器,它自动释放程序不再需要的已分配块。这些块称为垃圾。自动回收堆存储的过程叫做垃圾收集。
9.10.1 垃圾收集器的基本知识
垃圾收集器将内存视为一张有向可达图,该图的节点被分成一组根节点和一组堆节点。
当存在一条从任意根节点出发并到达p的有向路径时,我们说节点p是可达的。
9.10.2 Mark&Sweep垃圾收集器
Mark&Sweep垃圾收集器由标记阶段和清除阶段组成。
9.10.3 C程序的保守Mark&Sweep
9.11 C程序中常见的与内存有关的错误
9.11.1 间接引用坏指针
在这种情况下,scanf将把val的内容解释为一个地址,并试图将一个字写到这个位置。在最好的情况下,程序立即以异常中止。在最糟糕的情况下,val的内容对应于虚拟内存的某个合法的读/写区域,于是我们就覆盖了这块内存,这通常会在相当长的段时间以后造成灾难性的、令人困惑的后果。
9.11.2 读未初始化的内存
一个常见的错误就是假设堆内存被初始化为零:
9.11.3 允许栈缓冲区溢出
如果一个程序不检查输入串的大小就写入栈中的目标缓冲区,那么这个程序就会有缓冲区溢出错误。
为了纠正错误,我们必须使用fgets函数,这个函数限制了输入串的大小。
9.11.4 假设指针和它们指向的对象是相同大小的
一种常见的错误是假设指向对象的指针和他们所指向的对象是大小相同的。
9.11.5 造成错位错误
错位错误是另一种很常见的造成覆盖错误的来源。
9.11.6 引用指针,而不是它所指向的对象
9.11.7 误解指针运算
另一种常见的错误是忘记了指针的算术操作是以它们指向的对象的大小为单位来进的,而这种大小单位并不一定是字节。
9.11.8 引用不存在的变量
不理解栈的规则,有时会引用不再合法的本地变量。
9.11.9 引用空闲堆块中的数据
一个相似的错误是引用已经被释放了的堆块中的数据。
9.11.10 引起内存泄漏
内存泄露是缓慢、隐形的杀手。
教材学习中的问题和解决过程
- 如何解决虚拟地址A是否合法的问题?
- 缺页处理程序搜索区域结构的链表,把A和每个区域结构中的vm_start和vm_end做比较。如果这个指令是不合法的,那么缺页处理程序就触发一个段错误,从而终止这个进程。
代码调试中的问题和解决过程
代码托管
(statistics.sh脚本的运行结果截图)
上周考试错题总结
下面代码中,对数组x填充后,采用直接映射高速缓存,所有对x和y引用的命中率为(D)
A .
1
B .
1/4
C .
1/2
D .
3/4
下面说法正确的是(ABCD)
A .
CPU通过内存映射I/O向I/O设备发命令
B .
DMA传送不需要CPU的干涉
C .
SSD是一种基于闪存或Flash的存储技术
D .
逻辑磁盘块的逻辑块号可以翻译成一个(盘面,磁道,扇区 )三元组。
计算下面磁盘的容量(C):4个盘片,100000个柱面,每条磁道400个扇区,每个扇区512个字节
A .
81.92GB
B .
40.96GB
C .
163.84
D .
327.68GB
有关RAM的说法,正确的是(ADEG)
A .
SRAM和DRAM掉电后均无法保存里面的内容。
B .
DRAM将一个bit存在一个双稳态的存储单元中
C .
一般来说,SRAM比DRAM快
D .
SRAM常用来作高速缓存
E .
DRAM将每一个bit存储为对一个电容充电
F .
SRAM需要不断刷新
G .
DRAM被组织为二维数组而不是线性数组
其他(感悟、思考等,可选)
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 |
| 第一周 | 20/20 | 1/2 | 10/15 | 第一章 |
| 第三周 | 130/210 | 1/2 | 21/36 | 第二章 |
|第四周 | 70/ 280 | 1/4 | 10/46 | 第十章 |
|第五周 | 91 / 371 | 1/6 | 23/69 | 第三章 |
|第六周 | 308 / 648 | 1/8 | 31/100 | 第八、十章 |
|第七周 | 2200 / 2848 | 1/10 | 25/125 | 第四章 |
|第八周 | 1072 / 3492 | 1/12 | 41/166 | 第十一,十二章 |
|第九周 | 508/4000 | 1/14 | 30/196 | 第六章 |
|第十周 | 1377 / 3492 | 1/14 | 12/208 | 实验 实时系统|
|第十一周 | 350 / 5727 | 1/16 | 35/243 | 第九章 |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
-
计划学习时间:30小时
-
实际学习时间:35小时
-
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)