20145221 《信息安全系统设计基础》第14周学习总结
20145221 《信息安全系统设计基础》第14周学习总结
虚拟存储器
- 虚拟存储器3个重要的能力:
- 将主存看作是一个存储在磁盘上的地址空间的高速缓存,在主存中只保护活动的区域,并根据需要在磁盘和主存之间来回传送数据;
- 为每个进程提供了一致的地址空间,从而简化了存储器管理;
- 保护了每个进程的地址空间不被其它进程破坏。
- 程序员需要理解虚拟存储器的原因:
- 虚拟存储器是中心的:它是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的交互中心;
- 虚拟存储器是强大的:它可以创建和销毁存储器片、可以映射存储器片映射到磁盘某个部分等等;
- 虚拟存储器若操作不当则十分危险
物理和虚拟寻址
- 物理地址PA
- 计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组,每字节都有一个唯一的物理地址PA。根据物理地址寻址的是物理寻址。
- 地址翻译
- 使用虚拟寻址时,CPU通过生成一个虚拟地址VA来访问主存,这个虚拟地址在被送到存储器之前先转换成适当的物理地址,相关硬件为存储器管理单元MMU。
地址空间
- 线性地址空间:地址空间中的整数是连续的。
- 虚拟地址空间:CPU从一个有 N=2^n 个地址的地址空间中生成虚拟地址,这个地址空间成为称为虚拟地址空间。
- 地址空间的大小:由表示最大地址所需要的位数来描述。
- 物理地址空间:与系统中的物理存储器的M个字节相对应。 5、虚拟存储器的基本思想:主存中的每个字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。
虚拟存储器作为缓存的工具
- 任意时刻,虚拟页面的集合都被分为三个不相交的子集:
- 未分配的:VM系统还没分配/创建的页,不占用任何磁盘空间。
- 缓存的:当前缓存在物理存储器中的已分配页
- 未缓存的:没有缓存在物理存储器中的已分配页
- 虚拟存储器:虚拟页VP,每个虚拟页大小为P=2^平字节
- 物理存储器——物理页PP,也叫页帧,大小也为P字节。
DRAM缓存的组织结构
- 不命中处罚很大
- 是全相联的——任何虚拟页都可以放在任何的物理页中。
- 替换算法精密
- 总是使用写回而不是直写。
页表
- 页表:是一个数据结构,存放在物理存储器中,将虚拟页映射到物理页,就是一个页表条目的数组。
页表就是一个页表条目PTE的数组。- PTE:由一个有效位和一个n位地址字段组成的,表明了该虚拟页是否被缓存在DRAM中。 页表的组成:有效位+n位地址字段
- 如果设置了有效位
- 地址字段表示DRAM中相应的物理页的起始位置,这个物理页中缓存了该虚拟页。
- 如果没有设置有效位:
- 空地址:表示该虚拟页未被分配
- 不是空地址:这个地址指向该虚拟页在磁盘上的起始位置。
页命中
- 当CPU读取一个字的时候,地址翻译硬件将虚拟地址作为一个索引来定位PTE,并从存储器中读取它。
缺页
- DRA缓存不命中称为缺页。在不命中发生时,换入页面的策略称为按需页面调度。
- 颠簸:工作集大小超出了物理存储器的大小。
虚拟存储器作为存储器管理工具
- 按需页面调度和独立的虚拟地址空间的结合简化了链接和加载、代码和数据共享,以及应用程序的存储器分配。
- 简化链接:独立的地址空间允许每个进程的存储器映像使用相同的基本格式,而不管代码和数据实际存放在物理存储器的何处。
- 简化加载:虚拟存储器使得容易想存储器中加载可执行文件和共享文件对象。
- 简化共享:独立地址空间为操作系统提供了一个管理用户进程和操作系统自身之间共享的一致机制。
- 简化存储器分配:虚拟存储器为向用户进程提供一个简单的分配额外存储器的机制。
虚拟存储器作为存储器保护的工具
- PTE的三个许可位:
- SUP:表示进程是否必须运行在内核模式下才能访问该页
- READ:读权限
- WRITE:写权限
地址翻译
-
地址翻译:一个N元素的虚拟地址空间(VAS)中的元素和一个M元素的物理地址空间(PAS)之间的映射。
-
MAP:VAS→PAS∪空 MAP(A)
=- A':如果虚拟地址A处的数据在PAS的物理地址A处'
- 空:如果虚拟地址A处的数据不在物理存储器中
-
CPU中的一个控制寄存器页表基址寄存器指向当前页表,n位的虚拟地址包含两个部分:一个p位的虚拟页面偏移(VPO) 和一个(n-p)位的虚拟页号,页表条目中的物理页页号和虚拟地址中的VPO串联起来,就得到了相应的物理地址。
结合高速缓存和虚拟存储器
- 主要思路:地址翻译发生在高速缓存之前,页表目录可以缓存,就像其他的数据字一样。
利用TLB加速地址翻译
- TLB:翻译后备缓冲器,是一个小的、虚拟存储的缓存,其中每一行都保存着一个由单个PTE组成的块。
多级页表
- 多级页表——采用层次结构,用来压缩页表。
- 以两层页表层次结构为例,好处是:
- 如果一级页表中的一个PTE是空的,那么相应的二级页表就根本不会存在
- 只有一级页表才需要总是在主存中,虚拟存储器系统可以在需要时创建、页面调入或调出二级页表,只有最经常使用的二级页表才缓存在主存中。
存储器映射
- 存储器映射:Linux通过将一个虚拟存储器区域与一个磁盘上的对象关联起来,以初始化这个虚拟存储器区域的内容的过程。
- 映射对象:
- Unix文件系统中的普通文件
- 匿名文件(全都是二进制0)
- 一旦一个虚拟页面被初始化了,它就在一个由内核维护的专门的交换文件之间换来换去。交换文件也叫交换空间,或交换区域。
共享对象
- 一个对象被映射到虚拟存储器的一个区域,要么作为共享对象,要么作为私有对象。
- 一个映射到共享对象的虚拟存储器区域叫做共享区域,类似的,也有虚拟区域。
- 私有对象时使用一种叫做写时拷贝的技术被映射到虚拟存储器中的,相应私有区域的页表条目都被标记为只读,并且区域结构被标记为私有的写时拷贝。
execve函数
- 使用execve函数将
a.out
程序加载到存储器的过程Execve("a.out",NULL,NULL);
- 具体经过的步骤如下:
- 删除已存在的用户区域。
- 映射私有区域。
- 映射共享区域。
- 设置程序计数器。
使用map函数的用户级存储器映射
- Unix进程可以使用mmap函数来创建新的虚拟存储器区域,并将对象映射到这些区域当中
void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offest); 若成功则为指向映射区域的指针,若出错则为MAP_FAILDE(-1)
- munmap函数删除虚拟存储器的区域。
int munmap(void *start,size_t length); 若成功则返回0,若失败则返回-1.
动态存储器分配
- 当运行时需要额外虚拟存储器时,使用动态存储器分配器维护一个进程的虚拟存储器区域。
- 分配器有两种风格。
- 显示分配器:要求应用显式地释放任何已经分配的块。
- 隐式分配器:要求分配器检测一个已分配块何时不再被程序所使用,就释放这个块。也叫做垃圾收集器。
malloc和free函数
-
系统调用malloc函数,从堆中分配块:
void *malloc(size_t size); 成功返回指针,指向大小至少为size字节的存储器块,失败返回NULL
-
系统调用free函数来释放已分配的堆块:
void free(void *ptr); 无返回值,ptr参数必须指向一个从malloc、calloc或者reallov获得的已分配块的起始位置。 ```
-
使用动态存储器分配原因:经常直到程序实际运行时,才知道某些数据结构的大小。
分配器的要求和目标
- 显示分配器的要求:
- 处理任意请求序列
- 立即响应请求
- 只使用堆
- 对齐块
- 不修改已分配的块
- 目标:
- 最大化吞吐率:最大化存储器利用率——峰值利用率最大化
- 吞吐率:每个单位时间里完成的请求数
碎片
- 碎片:虽然有未使用的存储器,但是不能用来满足分配请求。
- 内部碎片:发生在一个已分配块比有效载荷大的时候,易于量化。
- 外部碎片:发生在当空闲存储器合计起来足够满足一个分配请求,但是没有一个单独的空间块足以处理这个请求时发生。难以量化,不可预测。
垃圾收集
- 垃圾收集器:一种动态存储分配器,它自动释放程序不再需要的已分配块,这些块被称为垃圾,自动回收堆存储的过程叫做垃圾收集。
垃圾收集器的基本知识
- 垃圾收集器将存储器视为一张有向可达图,图的节点被分配为一组根节点和一组堆节点。当存在一条从任意根节点出发到并到达P的有向路径时,就称节点P是可达的。
Mark&Sweep垃圾收集器
- Mark&Sweep垃圾收集器由标记阶段和清除阶段组成,标记阶段标记出根节点所有可达的和已分配的后继,清除阶段释放每个未被标记的已分配块。
- 在对Mark&Sweep的描述中使用下列函数
ptr isPtr(ptr p)
:如果p指向一个已分配块中的某个字,那么就返回一个指向这个块起始位置的指针b,否则返回NULL。int blockMarked(ptr b)
:如果已经标记了块b,就返回true。int blockAllocated(ptr b)
:如果块b是已分配的,就返回true。void markBlock(ptr b)
:标记块b。int length(ptr b)
:返回块b的以字为单位的长度(不包括头部)。void unmarkBlock(ptr b)
:将块b的状态由已标记的改为未标记的。ptr nextBlock(ptr b)
:返回堆中块b的后继。
C语言中常见的与存储器有关的错误
间接引用坏指针
- 在进程的虚拟地址空间中有较大的洞,没有映射到任何有意义的数据,如果试图引用一个指向这些洞的指针,操作系统就会以段异常来终止程序。 典型的错误是: scanf("%d",val);
读未初始化的存储器
- 虽然bass存储器位置总是被加载器初始化为0,但对于堆存储器却并不是这样的。 常见的错误就是假设堆存储器被初始化为0.
允许栈缓冲区溢出
- 如果一个程序不检查输入串的大小就写入栈中的目标缓冲区,程序就会出现缓冲区溢出错误。
假设指针和指向他们的对象大小是相同的。
- 一种常见的错误是假设指向对象的指针和他们所指向的对象是大小相同的。
造成错位错误。
- 一种很常见的覆盖错误来源
引用指针,而不是他所指向的对象。
- 注意C的优先级和结合性
误解指针运算
- 忘记了指针的算术操作是以它们指向的对象的大小为单位来进行,而这种大小单位不一定是字节。
引用不存在的变量。
- 不理解栈的规则,有时会引用不再合法的本地变量。
引用空闲堆块中的数据。
- 不理解栈的规则,有时会引用不再合法的本地变量。
引起存储器泄露
- 当不小心忘记释放已分配块,而在堆里创建了垃圾时,就会引起存储器泄露。
代码托管
-
代码统计:
其他(感悟、思考等,可选)
- 这周内容有点抽象,总体来说有点难理解,还得慢慢消化啊!
- 最后一部分关于C语言的讲述感觉特亲切,其中的几种错误之前练习中就遇到过,也大致能弄懂为什么会出错,但是这次的讲解更系统全面。前半部分讲述了虚拟存储器是如何工作的,模拟推导了其工作过程,而后半部分描述的是应用程序如何使用和管理虚拟存储器的,这也让我从存储器的角度再理解了一些C语言中的错误。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | 学习了Linux常用命令 |
第二周 | 79/279 | 1/3 | 30/50 | 了解vim,gcc,gdb基本操作 |
第三周 | 182/461 | 1/4 | 25/75 | 更深层次了解信息处理 |
第四周 | 36/497 | 2/6 | 3/78 | 第二章知识简单的运用 |
第五周 | 194/691 | 1/7 | 28/106 | 汇编知识与了解逆向 |
第六周 | 520/1211 | 1/8 | 27/133 | Y86处理器,了解ISA抽象 |
第七周 | 85/1296 | 1/9 | 21/153 | 理解了局部性原理 |
第八周 | 0/1296 | 2/11 | 20/173 | 期中总结 |
第九周 | 234/1530 | 2/13 | 30/203 | 了解Linux是怎样操作文件 |
第十周 | 453/1983 | 2/15 | 20/223 | 系统调用深入实践 |
第十一周 | 1097/3080 | 3/18 | 26/249 | 理解进程和并发,了解异常及其种类 |
第十二周 | 0/3080 | 3/18 | 20/269 | 复习巩固加深 |
第十三周 | 1005/4085 | 1/19 | 26/295 | 了解并发 |
第十四周 | 509/4594 | 1/20 | 20/315 | 虚拟存储器的组织结构 |