2017-2018-1 20155326 《信息安全系统设计基础》第11周学习总结

教材学习内容总结

虚拟存储器是计算机系统最重要的概念之一,它是对主存的一个抽象

三个重要能力:

  • 它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,高效的使用了主存

  • 它为每个进程提供了一致的地址空间,从而简化了存储器管理

  • 它保护了每个进程的地址空间不被其他进程破坏

物理和虚拟寻址

1.物理地址

  • 计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组,每字节都有一个唯一的物理地址PA。

  • 根据物理地址寻址的是物理寻址。

2.虚拟地址

  • 虚拟存储器被组织为一个由存放在磁盘上的N个连续的字节大小的单元组成的数组。

  • 使用虚拟寻址时,CPU通过生成一个虚拟地址VA来访问主存,这个虚拟地址在被送到存储器之前先转换成适当的物理地址(这个过程叫做地址翻译,相关硬件为存储器管理单元MMU)

地址空间

1.地址空间

  • 地址空间是一个非负整数地址的有序集合:

2.线性地址空间

  • 地址空间中的整数是连续的。

3.虚拟地址空间

  • CPU从一个有 N=2^n 个地址的地址空间中生成虚拟地址,这个地址空间成为称为虚拟地址空间。

4.地址空间的大小

  • 由表示最大地址所需要的位数来描述。

  • N=2^n:n位地址空间

  • 主存中的每个字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。

虚拟存储器作为缓存的工具

  • 虚拟存储器——虚拟页VP,每个虚拟页大小为P=2^平字节

  • 物理存储器——物理页PP,也叫页帧,大小也为P字节。

  • 任意时刻,虚拟页面的集合都被分为三个不相交的子集:

    未分配的:VM系统还没分配/创建的页,不占用任何磁盘空间。
    缓存的:当前缓存在物理存储器中的已分配页
    未缓存的:没有缓存在物理存储器中的已分配页

  • DRAM缓存的组织结构

    不命中处罚很大
    是全相联的——任何虚拟页都可以放在任何的物理页中。
    替换算法精密
    总是使用写回而不是直写。

  • 页表是一个数据结构,存放在物理存储器中,将虚拟页映射到物理页。

  • 页表就是一个页表条目PTE的数组,组成为:

    有效位+n位地址字段

    如果设置了有效位,地址字段就表示DRAM中相应的物理页的起始位置,这个物理页中缓存了该虚拟页。
    如果未设置有效位,一个空地址表示虚拟页未分配,否则,这个地址就指向该虚拟页在磁盘中的起始位置。

  • 页:虚拟存储器的习惯说法,就是块

  • 缺页:就是指DRAM缓存不命中。

  • 缺页异常:会调用内核中的缺页异常处理程序,选择一个牺牲页。

  • 交换=页面调度:磁盘和存储器之间传送页的活动

  • 按需页面调度:直到发生不命中时才换入页面的策略,所有现代系统都使用这个。

  • 局部性原则保证了在任意时刻,程序将往往在一个较小的活动页面集合上工作,这个集合叫做工作集/常驻集。

  • 所以只要程序有良好的时间局部性,虚拟存储器系统就能工作的相当好。

虚拟存储器作为存储器管理的工具

  • 操作系统为每个进程提供了一个独立的页表,也就是一个独立的虚拟地址空间。

  • 单个虚拟页面可以映射到同一个共享物理页面上。

  • 存储器映射:将一组连续的虚拟页映射到任意一个文件中的任意位置的表示法。

  • VM简化了链接和加载、代码和数据共享,以及应用程序的存储器分配。

虚拟存储器作为存储器保护的工具

PTE的三个许可位:

SUP:表示进程是否必须运行在内核模式下才能访问该页
READ:读权限
WRITE:写权限

地址翻译

  • 地址翻译就是一个N元素的虚拟地址空间VAS中的元素和一个M元素的物理地址空间PAS中元素之间的映射。

  • 页面基址寄存器PTBR指向当前页表。

  • 当页面命中时,CPU动作:

    处理器生成虚拟地址,传给MMU
    MMU生成PTE地址,并从高速缓存/主存请求得到他
    高速缓存/主存向MMU返回PTE
    MMU构造物理地址,并把它传给高速缓存/主存
    高速缓存/主存返回所请求的数据给处理器。

  • 处理缺页时:

    处理器生成虚拟地址,传给MMU
    MMU生成PTE地址,并从高速缓存/主存请求得到他
    高速缓存/主存向MMU返回PTE
    PTE中有效位为0,触发缺页异常
    确定牺牲页
    调入新页面,更新PTE
    返回原来的进程,再次执行导致缺页的指令,会命中

  • 结合高速缓存和虚拟存储器来看

在既使用SRAM高速缓存又使用虚拟存储器的系统中,大多数系统选择物理寻址
主要思路是地址翻译发生在高速缓存之前

页表目录可以缓存,就像其他的数据字一样

  • 利用TLB加速地址翻译

TLB:翻译后备缓冲器,是一个小的、虚拟存储的缓存,其中每一行都保存着一个由单个PTE组成的块

  • 步骤:

    CPU产生一个虚拟地址
    MMU从TLB中取出相应的PTE
    MMU将这个虚拟地址翻译成一个物理地址,并且将它发送到高速缓存/主存
    高速缓存/主存将所请求的数据字返回给CPU

多级页表

  • 多级页表——采用层次结构,用来压缩页表。

  • 两层页表层次结构好处是:

如果一级页表中的一个PTE是空的,那么相应的二级页表就根本不会存在

只有一级页表才需要总是在主存中,虚拟存储器系统可以在需要时创建、页面调入或调出二级页表,只有最经常使用的二级页表才缓存在主存中。

存储器映射

  • 即指Linux通过将一个虚拟存储器区域与一个磁盘上的对象关联起来,以初始化这个虚拟存储器区域的内容的过程。

  • 映射对象:

    Unix文件系统中的普通文件

    匿名文件(全都是二进制0)

  • 共享对象

共享对象对于所有把它映射到自己的虚拟存储器进程来说都是可见的

即使映射到多个共享区域,物理存储器中也只需要存放共享对象的一个拷贝。

  • 私有对象

私有对象运用的技术:写时拷贝

在物理存储器中只保存有私有对象的一份拷贝

fork函数就是应用了写时拷贝技术,至于execve函数:

  • 使用mmap函数的用户级存储器映射

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

参数含义:

start:这个区域从start开始
fd:文件描述符
length:连续的对象片大小
offset:距文件开始处的偏移量
prot:访问权限位,具体如下:
PROT_EXEC:由可以被CPU执行的指令组成
PROT_READ:可读
PROT_WRITE:可写
PROT_NONE:不能被访问
flag:由描述被映射对象类型的位组成,具体如下:
MAP_ANON:匿名对象,虚拟页面是二进制0
MAP_PRIVATE:私有的、写时拷贝的对象
MAP_SHARED:共享对象

2.删除虚拟存储器:

	#include <unistd.h>
	#include <sys/mman.h>
	int munmap(void *start, size_t length);

成功返回0,失败返回-1

从start开始删除,由接下来length字节组成的区域。

动态存储器分配

  • 堆:

是一个请求二进制0的区域,紧接在未初始化的bss区域后开始,并向上(更高的地址)生长。有一个变量brk指向堆的顶部

  • 分配器的两种基本风格:

    显示分配器-malloc和free

    隐式分配器/垃圾收集器

  • malloc和free函数:

系统调用malloc函数,从堆中分配块:

	#include <stdlib.h>

	void *malloc(size_t size);

成功返回指针,指向大小至少为size字节的存储器块,失败返回NULL
系统调用free函数来释放已分配的堆块:

	#include <stdlib.h>

	void free(void *ptr);

无返回值

ptr参数必须指向一个从malloc、calloc或者reallov获得的已分配块的起始位置。

  • 为什么要使用动态存储器分配?

    因为经常知道程序实际运行时,它们才知道某些数据结构的大小。

  • 实现简单的分配器

有几点需要注意:

序言块和结尾块:序言块是初始化时创建的,而且永不释放;结尾块是一个特殊的块,总是以它为结束。

有一个技巧,就是将重复使用的,操作复杂又有重复性的,这些可以定义成宏,方便使用也方便修改。

需要注意强制类型转换,尤其是带指针的,非常复杂。

因为规定了字节对齐方式为双字,就代表块的大小是双字的整数倍,不是的舍入到是。

  • 显式空闲链表

1.区别

(1)分配时间

隐式的,分配时间是块总数的线性时间

但是显式的,是空闲块数量的线性时间。

(2)链表形式

隐式——隐式空闲链表

显式——双向链表,有前驱和后继,比头部脚部好使。

2.排序策略:

后进先出,
按照地址顺序维护

  • 分离的空闲链表

分离存储,是一种流行的减少分配时间的方法。一般思路是将所有可能的块大小分成一些等价类/大小类。

分配器维护着一个空闲链表数组,每个大小类一个空闲链表,按照大小的升序排列。

有两种基本方法:

1.简单分离存储

每个大小类的空闲链表包含大小相等的块,每个块的大小就是这个大小类中最大元素的大小。

(1)操作

如果链表非空:分配其中第一块的全部

如果链表为空:分配器向操作系统请求一个固定大小的额外存储器片,将这个片分成大小相等的块,并且连接起来成为新的空闲链表。

(2)优缺点

优点:时间快,开销小

缺点:容易造成内部、外部碎片

2.分离适配

每个空闲链表是和一个大小类相关联的,并且被组织成某种类型的显示或隐式链表,每个链表包含潜在的大小不同的块,这些块的大小是大小类的成员。

这种方法快速并且对存储器使用很有效率。

3.伙伴系统——分离适配的特例

其中每个大小类都是2的幂

这样,给定地址和块的大小,很容易计算出它的伙伴的地址,也就是说:一个块的地址和它的伙伴的地址只有一位不同。

优点:快速检索,快速合并。

垃圾收集

  • 垃圾收集器是一种动态存储分配器,它自动释放程序不再需要的已分配块,这些块被称为垃圾,自动回收堆存储的过程叫做垃圾收集。

  • Mark&Sweep垃圾收集器

标记:标记出根节点的所有可达的和已分配的后继

清楚:释放每个未被标记的已分配块。

  • C保守的Mark&Sweep——平衡二叉树

根本原因是C语言不会用类型标记来标记存储器位置。

C程序中常见的与存储器有关的错误

1.间接引用坏指针

  • 常见错误:

——scanf错误

2.读未初始化的存储器

  • 常见错误:

——假设堆存储器被初始化为0

3.允许栈缓冲区溢出

  • 常见错误:

——缓冲区溢出错误

4.假设指针和它们指向的对象是相同大小的

在远处起作用action at distance

5.造成错位错误

6.引用指针,而不是它所指向的对象

7.误解指针运算

8.引用不存在的变量

9.引用空堆块中的数据

10.引起存储器泄露

代码托管

(statistics.sh脚本的运行结果截图)

上周考试错题总结

结对及互评

本周结对学习情况

- [20155320](http://www.cnblogs.com/ljq1997/p/7967042.html)
- ![](https://images2018.cnblogs.com/blog/1071525/201712/1071525-20171203212026304-849425141.jpg)


- 结对学习内容
    - 第9章 虚拟内存
    - ...

其他(感悟、思考等,可选)

这章的内容概念性的东西很多,需要理解才能记得更长久。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 0/0 1/1 10/20
第二周 57/100 1/1 20/30
第三周 100/100 1/1 30/30
第四周 233/200 1/1 20/20
第五周 267/200 1/1 20/20
第六周 220/2000 3/1 40/20
第七周 362/300 1/1 30/30
第八周 3000/300 3/1 45/30
第九周 596/300 3/1 45/30
第十周 154/300 3/1 40/30

尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。

参考:软件工程软件的估计为什么这么难软件工程 估计方法

  • 计划学习时间:XX小时

  • 实际学习时间:XX小时

  • 改进情况:

(有空多看看现代软件工程 课件
软件工程师能力自我评价表
)

参考资料