这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战
单进程
单个进程,进程独占整个内存空间。为满足多进程的内存占用。假设整个内存大小4G,可以给每个进程分配指定空间大小,比如每个进程1G。但是如此只能同时运行4个进程,并且每个进程占用的空间很少,并且每个进程使用的内存地址位置也不同,给进程寻址带来了困难,并且这种访问内存的方式会让A进程能够访问到B进程的地址发空间,安全性很差。
基地址+偏移
基于以上问题,提出了【基地址+偏移】的方式,进程启动时,为其分配一个基地址,进程的地址空间从0开始,访问地址时物理地址=基地址+虚拟地址。硬件中提供了基地址寄存器与界限寄存器来进行地址转换的操作。
分段
由于基地址的方式内部的堆向下增长,栈向上增长,中间的空间如果未使用会白白浪费。所以提出了将 栈和堆都分配 基地址寄存器和界限寄存器。如果物理地址空间为64字节,访问地址需要6位表示。在分段情况下,可以将第一位用作判断是堆的分段还是栈的分段。后面5位表示对应段的偏移量。当然每个段的基地址需要保存在进程的元数据信息中。分段用于解决内部空间使用率低问题。
分页
由于分段的方式每次分配的内存大小不一,在分配回收后,会产生小块的内存碎片,如果每次分配的内存大小大于小块,会导致小块内存虽然未使用但是永远不会被分配,回收小块内存只能靠移动内存,将空间小块合并为连续的空闲大块,但是这种方式效率太低。因此,将物理空间分割为固定的大小,回收分配比较快捷,毕竟回收时回收整个块,分配直接分配就可以,并且也不需要物理空间连续,只需要虚拟空间连续即可。这种方式称为分页,固定块称为一个页,为了采用分页方式,需要存储虚拟页到物理页的地址映射,这部分明显也需要存储到进程的元数据中。比如32位的地址空间,假设每页4kb=2^12,则需要2^20个映射,假设每个映射占用4byte空间,则2^20个映射需要 4mb的空间(2^20 * 4 / 1024 / 1024)。目前操作系统基本都运行几十上百进程,如果采用这种方式,那么分页机制的页表开销就需要上百mb空间。
多级页表
为了解决页表内存占用大的问题。提出了多级页表,即将2^20个映射也用页表进行表示,假设每个映射占用4byte空间,一页可以保存 4kb / 4 = 1024=2^10个映射,我们将这种页表的页表称作页目录,只需要1024个页目录,即可表示2^20个页表。因此如果引入页目录,需要的内存空间是:
一级页表+二级页表 = 4mb + 4kb =4.004mb,引入多集页表不是减少内存吗?为什么还变大了呢?
从页表性质来看,页表需要覆盖到整个地址空间,也就是32位的寻址空间,如果不分级,一级页表覆盖整个地址空间需要4mb空间。而分级后,只需要4kb的二级页表即可覆盖整个地址空间,并且通常进程不会占满整个32位的地址空间,假若仅仅使用了20%的一级页表,那么占用的空间大小=4kb + 0.2 * 1k * 1k * 4 = 0.804 mb。