20145219 《信息安全系统设计基础》第14周学习总结
教材学习内容总结
地址
-
物理和虚拟寻址
1、物理寻址
主存被组织成一个由M个连续的字节大小的单元组成的数组,依次类推的寻址方式。
2、虚拟寻址
CPU生成一个虚拟地址(VA)来访问主存,在被传送到存储器之前先转换成适当的物理地址。
地址翻译通过CPU芯片上的存储器管理单元完成。
-
地址空间
一个非负整数地址的有序集合:{0,1,2,……}。
主存中的每个字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。
1、线性地址空间
有序集合中整数是连续的。
2、虚拟地址空间
一个有 N=2^n 个地址的地址空间,CPU从其中生成虚拟地址。
3、物理地址空间
与系统中的物理存储器的M个字节相对应。
4、地址空间的大小
由表示最大地址所需要的位数来描述:
N=2^n:n位地址空间
。
虚拟存储器
-
三个重要能力
- 它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,高效的使用了主存
- 它为每个进程提供了一致的地址空间,从而简化了存储器管理
- 它保护了每个进程的地址空间不被其他进程破坏
-
作为缓存的工具
1、页面
- 虚拟页VP(虚拟存储器),每个虚拟页大小为P=2^平字节。
- 物理页PP(物理存储器),也叫页帧,大小也为P字节。
任意时刻,虚拟页面的集合都被分为三个不相交的子集:
未分配的:VM系统还没分配、创建的页,不占用任何磁盘空间。 缓存的:当前缓存在物理存储器中的已分配页。 未缓存的:没有缓存在物理存储器中的已分配页。
2、页表
一个数据结构,存放在物理存储器中,将虚拟页映射到物理页。
(1)页表就是一个页表条目PTE的数组。
PTE:由一个有效位和一个n位地址字段组成的,表明了该虚拟页是否被缓存在DRAM中。
PTE的三个许可位: SUP:表示进程是否必须运行在内核模式下才能访问该页 READ:读权限 WRITE:写权限
(2)页表的组成:有效位+n位地址字段
-
设置了有效位:地址字段表示DRAM中相应的物理页的起始位置,这个物理页中缓存了该虚拟页。
-
没有设置有效位:
空地址:表示该虚拟页未被分配 不是空地址:这个地址指向该虚拟页在磁盘上的起始位置。
3、缺页
指DRAM缓存不命中。
缺页异常:会调用内核中的缺页异常处理程序,选择一个牺牲页。
页面调度(交换):磁盘和存储器之间传送页的活动。
按需页面调度:直到发生不命中时才换入页面的策略,所有现代系统都使用这个。
4、虚拟存储器中的局部性
局部性原则:保证了在任意时刻,程序将往往在一个较小的活动页面集合上工作,这个集合叫做工作集/常驻集。
颠簸:工作集大小超出了物理存储器的大小。
只要程序有良好的时间局部性,虚拟存储器系统就能工作的相当好。
-
作为存储器管理的工具
操作系统为每个进程提供了一个独立的页表,也就是一个独立的虚拟地址空间。
存储器映射:将一组连续的虚拟页映射到任意一个文件中的任意位置的表示法。
多个虚拟页面可以映射到同一个共享物理页面上。
按需页面调度和独立的虚拟地址空间的结合简化了链接和加载、代码和数据共享,以及应用程序的存储器分配。
-
作为存储器保护的工具
通过在PTE上添加一些额外的许可来控制对一个虚拟页面的内容访问。
-
地址翻译
1、地址翻译
一个N元素的虚拟地址空间VAS中的元素和一个M元素的物理地址空间PAS中元素之间的映射。
MAP: VAS → PAS ∪ ∅ MAP = A' ,如果虚拟地址A处的数据在PAS的物理地址A'处 MAP = ∅ ,如果虚拟地址A处的数据不在物理存储器中
用页表实现映射
-
页面命中时,CPU硬件执行步骤:
处理器生成虚拟地址,传给MMU MMU生成PTE地址,并从高速缓存/主存请求得到他 高速缓存/主存向MMU返回PTE MMU构造物理地址,并把它传给高速缓存/主存 高速缓存/主存返回所请求的数据给处理器。
-
处理缺页时,CPU硬件执行步骤
处理器生成虚拟地址,传给MMU MMU生成PTE地址,并从高速缓存/主存请求得到他 高速缓存/主存向MMU返回PTE PTE中有效位为0,触发缺页异常 确定牺牲页 调入新页面,更新PTE 返回原来的进程,再次执行导致缺页的指令,会命中
2、结合高速缓存和虚拟存储器
在既使用SRAM高速缓存又使用虚拟存储器的系统中,大多数系统选择物理寻址。
两者结合的主要思路是地址翻译发生在高速缓存之前。
页表目录可以缓存,就像其他的数据字一样。
3、利用TLB加速地址翻译
TLB:翻译后备缓冲器,是一个小的、虚拟存储的缓存,其中每一行都保存着一个由单个PTE组成的块。
步骤:
CPU产生一个虚拟地址 MMU从TLB中取出相应的PTE MMU将这个虚拟地址翻译成一个物理地址,并且将它发送到高速缓存/主存 高速缓存/主存将所请求的数据字返回给CPU
4、多级页表
采用层次结构,用来压缩页表。
好处:
- 如果一级页表中的一个PTE是空的,那么相应的二级页表就根本不会存在。
- 只有一级页表才需要总是在主存中,虚拟存储器系统可以在需要时创建、页面调入或调出二级页表,只有最经常使用的二级页表才缓存在主存中。
地址翻译:
-
案例研究
-
core i7地址翻译
PTE的三个权限位:
R/W位:确定内容是读写还是只读 U/S位:确定是否能在用户模式访问该页 XD位:禁止执行位,64位系统中引入,可以用来禁止从某些存储器页取指令
1、linux为每个进程维持了一个单独的虚拟地址空间,其中,内核虚拟存储器位于用户栈之上。
内核虚拟存储器包含内核中的代码和数据结构,还有一些被映射到一组连续的物理页面(主要是便捷地访问特定位置,比如执行I/O操作的时候需要的位置)。
2、linux将虚拟存储器组织成一些区域(也叫做段)的集合。
允许虚拟地址空间有间隙;内核不用记录那些不存在的页,这样的页也不用占用存储器。
区域结构:
vm _start:指向这个区域的起始处; vm _end:指向这个区域的结束处; vm _prot:描述这个区域内所包含的所有页的读写许可权限; vm _fags:描述这个区域内的页面是与其他进程共享的,还是这个进程私有的,等等; vm _next:指向链表的下一个结构.
存储器
-
存储器映射
Linux通过将一个虚拟存储器区域与一个磁盘上的对象关联起来,以初始化这个虚拟存储器区域的内容的过程。
映射对象:
Unix文件系统中的普通文件 匿名文件(全都是二进制0)
1、共享对象
对于所有把它映射到自己的虚拟存储器进程来说都是可见的。即使映射到多个共享区域,物理存储器中也只需要存放共享对象的一个拷贝。
2、私有对象
运用写时拷贝的技术,在物理存储器中只保存有私有对象的一份拷贝。
3、fork函数:应用了写时拷贝技术。为每个进程保持了私有地址空间的抽象概念。
4、execve函数:将程序加载到存储器。
Execve("a.out",NULL,NULL);
步骤:
删除已存在的用户区域。 映射私有区域。 映射共享区域。 设置程序计数器。
5、map函数:
(1)创建新的虚拟存储器区域。
#include <unistd.h> #include <sys/mman.h> void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); //成功返回指向映射区域的指针,若出错则为-1
(2)删除虚拟存储器
#include <sys/mman.h>、 int munmap(void *start, size_t length); //成功返回0,失败返回-1
从start开始删除,由接下来length字节组成的区域。
-
动态存储器分配
堆:一个请求二进制0的区域,紧接在未初始化的bss区域后开始,并向上(更高的地址)生长。有一个变量brk指向堆的顶部。
使用动态存储器分配原因:经常直到程序实际运行时,才知道某些数据结构的大小。
分配器的两种基本风格:
显示分配器-malloc和free 隐式分配器/垃圾收集器
1、malloc函数:从堆中分配块。
#include <stdlib.h> void *malloc(size_t size); //成功返回指针,指向大小至少为size字节的存储器块,失败返回NULL
2、free函数:释放已分配的堆块。
#include <stdlib.h> void free(void *ptr); //无返回值,ptr参数必须指向一个从malloc、calloc或者reallov获得的已分配块的起始位置。
3、分配器的要求和目标
要求:
处理任意请求序列 立即响应请求 只使用堆 对齐块 不修改已分配的块
目标:
最大化吞吐率 最大化存储器利用率——峰值利用率最大化
4、碎片:虽然有未使用的存储器,但是不能用来满足分配请求。
- 内部碎片:发生在一个已分配块比有效载荷大的时候,易于量化。
- 外部碎片:发生在当空闲存储器合计起来足够满足一个分配请求,但是没有一个单独的空间块足以处理这个请求时发生。难以量化,不可预测。
5、隐式空闲链表
将堆组织成一个连续的已分配块和空闲块的序列:空闲块通过头部中的大小字段隐含地连接着,分配器可以通过遍历堆中所有的块,从而间接地遍历整个空闲块的集合。
需要特殊标记的结束块。
6、放置策略:放置已分配的块。
- 首次适配:从头开始搜索空闲链表,选择第一个合适的空闲块
- 下一次适配:从上一次搜索的结束位置开始搜索
- 最佳适配:检索每个空闲块,选择适合所需请求大小的最小空闲块
7、申请额外的堆存储器
#include <unistd.h> vid *sbrk(intptr_t incr); //成功则返回旧的brk指针,出错为-1
通过将内核的brk指针增加incr来扩展和收缩堆。
8、合并空闲块
合并是针对于假碎片问题的,任何实际的分配器都必须合并相邻的空闲块。
策略:
立即合并 推迟合并
-
垃圾收集
垃圾收集器是一种动态存储分配器,自动释放程序已经不再需要的已分配块(垃圾)。
垃圾收集器将存储器视为一张有向可达图,图的节点被分配为一组根节点和一组堆节点。
1、Mark&Sweep垃圾收集器
由标记阶段和清除阶段组成,标记阶段标记出根节点所有可达的和已分配的后继,清除阶段释放每个未被标记的已分配块。
C程序中常见的与存储器有关的错误
-
间接引用坏指针
在进程的虚拟地址空间中有较大的洞,没有映射到任何有意义的数据,如果试图引用一个指向这些洞的指针,操作系统就会以段异常来终止程序。
典型的错误:
scanf("%d",val);
-
读未初始化的存储器
bass存储器位置总是被加载器初始化为0,但对于堆存储器却并不是这样的。
典型的错误:假设堆存储器被初始化为0。
-
允许栈缓冲区溢出
如果一个程序不检查输入串的大小就写入栈中的目标缓冲区,程序就会出现缓冲区溢出错误。
-
假设指针和指向他们的对象大小是相同的
-
造成错位错误
一种很常见的覆盖错误来源。
-
引用指针,而不是他所指向的对象
注意C的优先级和结合性。
-
误解指针运算
忘记了指针的算术操作是以它们指向的对象的大小为单位来进行,而这种大小单位不一定是字节。
-
引用不存在的变量
不理解栈的规则,有时会引用不再合法的本地变量。
-
引用空闲堆块中的数据
-
引起存储器泄露
当不小心忘记释放已分配块,而在堆里创建了垃圾时,就会引起存储器泄露。
其他(感悟、思考等,可选)
本周学习的主要内容是存储器,从本章中,我巩固了之前在其他课中学习的一些知识,如:虚拟地址、寻址方式、缺页置换等,也学习了一些新的知识,如:LINUX 虚拟存储器系统、动态存储器分配、垃圾收集等,将不同课程的知识结合起来,融会贯通,有利于我们构建完整的知识体系、将知识活学活用,而不是单纯的背、记,只是为了应付考试。同时,这也是本学期书本学习内容的最后一章了,这一学期的学习也进入了尾声,但是求知的路途是永远没有尽头的。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第零周 | 0/0 | 1/1 | 15/15 | 安装虚拟机 |
第一周 | 0/0 | 1/2 | 25/40 | 学习Linux命令 |
第二周 | 62/62 | 1/3 | 25/65 | 学习C编程 |
第三周 | 176/238 | 1/4 | 20/85 | 学习数的表示和计算 |
第五周 | 57/295 | 1/5 | 20/105 | 学习汇编语言,了解逆向思想 |
第六周 | 150/445 | 1/6 | 25/130 | 学习Y86处理器和HCL硬件描述语言 |
第七周 | 115/560 | 1/7 | 20/150 | 学习存储器相关知识 |
第八周 | 0/560 | 2/9 | 30/180 | 期中总结 |
第九周 | 186/746 | 2/11 | 20/200 | Unix I/O及相关函数 |
第十周 | 423/1169 | 2/13 | 23/223 | man、grep、cp、echostate、fileinfo、filesize、ls、setecho、spwd、testioctl、who代码的理解与调试 |
第十一周 | 1034/2203 | 2/15 | 31/254 | 学习异常控制流,并理解、运行老师给的代码 |
第十二周 | 29/2232 | 2/17 | 23/277 | 复习前三周内容,继续理解、运行代码,学习signal相关视频 |
第十三周 | 821/3053 | 1/18 | 19/296 | 网络编程、并发、进程、多线程 |
第十四周 | 0/3053 | 1/19 | 15/311 | 虚拟存储器、虚拟地址、C语言中常见的相关错误 |