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

2017-2018-1 20155214 《信息安全系统设计基础》

第11周学习总结

学习目标

  • 理解虚拟存储器的概念和作用
  • 理解地址翻译的概念
  • 理解存储器映射
  • 掌握动态存储器分配的方法
  • 理解垃圾收集的概念
  • 了解C语言中与存储器有关的错误

虚拟存储器
虚拟存储器是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的完美交互,它为每个进程提供了一个大的、一致的和私有的地址空间。通过一个很清晰的机制,虚拟存储器提供了三个重要的能力:

(1)它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,它高效地使用了主存。
(2)它为每个进程提供了一致的地址空间,从而简化了存储器管理。
(3)它保护了每个进程的地址空间不被其他进程破坏。

Linux虚拟存储器系统

内核虚拟存储器包含内核中的代码和数据结构。内核虚拟存储器的某些区域被映射到所有进程共享的物理页面。例如,每个进程共享内核的代码和全局数据结构。

地址翻译

储存器映射
Linux(以及其他一些形式的Unix)通过将一个虚拟存储器区域与一个磁盘上的对象(object)关联起来,以初始化这个虚拟存储器区域的内容,这个过程称为存储器映射(memory mapping)。虚拟存储器区域可以映射到两种类型的对象的一种:

  • (1)Unix文件上的普通文件:一个区域可以映射到一个普通磁盘文件的连续部分,例如一个可执行目标文件。文件区(section)被分成页大小的片,每一片包含一个虚拟页面的初始化内容。因为按需进行页面高度,所以这些虚拟页面没有实际进行物理存储器,直到CPU第一次引用到页面(即发射一个虚拟地址,落在地址空间这个页面的范围之内)。如果区域文件区要大,那么就用零来填充这个区域的余下部分。

  • (2)匿名文件:一个区域也可以映射到一个匿名文件,匿名文件是由内核创建的,包含的全是二进制零。CPU第一次引用这样一个区域内的虚拟页面时,内核就在物理存储器中找到一个合适的牺牲页面,如果该页面被修改过,就将这个页面换出来,用二进制零覆盖牺牲页面并更新页表,将这个页面标记为是驻留在存储器中的。注意在磁盘和存储器之间没有实际的数据传送。因为这个原因,映射到匿名文件的区域中的页面有时也叫做请求二进制零的页(demand-zero page)。
    无论在哪种情况下,一旦一个虚拟页面被初始化了, 它就在一个由内核维护的专门的交换文件(swap file)之间换来换去。交换文件也叫做交换空间(swap space)或者交换区域(swap area)。需要意识到的很重要的一点,在任何时刻,交换空间都限制着当前运行着的进程能够分配的虚拟页面的总数。

动态储存器分配
1、需要额外的虚拟存储器时,使用一种动态存储器分配器(dynamic memory allocator)。一个动态存储器分配器维护着一个进程的虚拟存储器区域,称为堆(heap)。在大多数的unix系统中,堆是一个请求二进制0的区域;对于每个进程,内核维护着一个变量brk,它指向堆的顶部。

2、分配器将堆视为一组不同大小的块(block)的集合来维护。每个块就是一个连续的虚拟存储器组块(chunk),要么是已分配的,要么是未分配的。
1)显式分配器(explicit allocator):如通过malloc,free或C++中通过new,delete来分配和释放一个块。
2)隐式分配器(implicit allocator):也叫做垃圾收集器(garbage collector)。自动释放未使用的已分配的块的过程叫做垃圾回收(garbage collection)。
3、malloc不初始化它返回的存储器,calloc是一个基于malloc的包装(wrapper)函数,它将分配的存储器初始化为0。想要改变一个以前已分配的块的大小,可以使用realloc函数。
4、分配器必须对齐块,使得它们可以保存任何类型的数据对象。在大多数系统中,以8字节边界对齐。
不修改已分配的块:分配器只能操作或者改变空闲块。一旦被分配,就不允许修改或者移动它。
5、碎片(fragmentation)
有内部碎片(internal)和外部碎片(external)
外部碎片:在一个已分配块比有效载荷在时发生的。(如对齐要求,分配最小值限制等)
外部碎片:当空闲存储器合计起来足够满足一个分配请求,但是没有一个单独的空闲块足够大可以来处理这个请求时发生的。
6、隐式空间链表
放置分配的块的策略有:首次适配(first fit),下一次适配(next fit),和最佳适配(best fit)。
如果空闲块已经最大程度的合并,而仍然不能生成一个足够大的块,来满足要求的话,分配器就会向内核请求额外的堆存储器,要么是通过调用nmap,要么是通过调用sbrk函数;分配器都会将额外的(增加的)存储器转化成一个大的空闲块,将这个块插入到空闲链表中,然后将被请求的块放置在这个新的空闲块中。

理解垃圾收集
1、垃圾收集器将存储器视为一张有向可达图(reachability graph)。

2、垃圾收集器由标记(mark)阶段和清除(sweep)阶段组成。标记阶段标记出根节点的所有可达的和已分配的后继,而后面的清除阶段释放每个被标记的已分配块。典型地,块头部中空闲的低位中的一位来表示这个块是否被标记了。

存储器相关的错误

  • 间接引用坏指针
    在进程的虚拟地址空间中有较大的洞,没有映射到任何有意义的数据。如果我们试图间接引用一个指向这些洞的指针,那么操作系统就会以段异常终止我们的程序。而且,虚拟存储器的某些区域是只读的。试图写这些区域将造成以保护异常终止这个程序。

  • 读未初始化的存储器
    虽然.bss存储器位置(诸如未初始化的全局C变量)问题被加载器初始化为零,但是对于堆存储器却并不是这样的。一个常见的错误就是假设堆存储器被初始化为零

  • 造成错位错误
    错位(Off-by-one)错误是另一种很常见的覆盖错误发生的原因

  • 引用指针,而不是它所指向的对象
    如果我们不太注意C操作符的优先级和结合性,我们就会错误地操作指针,而不是期望操作指针所指向的对象

  • 误解指针运算
    另一种常见的错误是忘记了指针的算术操作是以它们指向的对象的大小为单位来进行的,而这种大小单位并不一定是字节。

  • 引用不存在的变量
    没有太多经验的C程序员不理解栈的规则,有时会引用不再合法的本地变量

  • 引用空闲堆块中的数据
    一个相似的错误是引用已经被释放了的堆块中的数据。

  • 引起存储器泄漏
    存储器泄漏是缓慢、隐性的杀手,当程序员不小心忘记释放已分配块,而在堆里创建了垃圾时,会发生这种问题。例如,下面的函数分配了一个堆块x,然后不释放它就返回。

posted @ 2017-12-03 14:34  曾士轩  阅读(172)  评论(0编辑  收藏  举报