连续内存分配时必须物理地址连续,这可能没有满足需求大小的空间。不连续,找到合适空间的机会更高,但是也多了一些麻烦。基本块,段式比较大,页式比较小。 连续分配的缺点
分配给程序的物理内存必须连续存在内外碎片内存分配的动态修改困难内存利用效率低 非连续分配的设计目标:提高内存利用率效率和管理灵活性允许一个程序的使用非连续的物理地址空间允许共享代码和数据支持动态加载和动态链接 需要解决的问题:实现虚拟地址和物理地址的转换(软件实现灵活开销大,硬件实现够用开销小) 段式存储分段比较大,页式存储分段比较小段 表示访问方式和存储数据等属性相同的一段地址空间(也就是下图所示,代码和数据分开放,像是分类管理) 对应一个连续的内存的块(也就是说只看一段,它本身的地址是连续的) 若干个段组成进程逻辑地址空间 逻辑地址由二元组(s,addr)(段号,段内偏移) 实际访问时,根据段号对应的段描述符在段表中找到基址是多少,同时将段内偏移量与这一段的长度做比较,如果不超过长度,那就继续访问,否则报错内存异常。将段基址加上段内偏移就得到了实际的物理地址。
页帧(Page Frame),把物理地址空间分为大小相同的基本分配单位,大小是2n(这样转换起来比较方便,在32位的机器中,4K是比较常见的大小) 页面(Page),把逻辑地址空间也划分为大小相同的基本分配单位,帧和页的大小必须是相同的。 页面到页帧的转换 -逻辑地址到物理地址的转换 -页表 -MMU/TLB
帧 内存物理地址的表示(f,o)(帧号,偏移量),另外每一页的大小为2s 假定16位地址空间,9位大小的页帧,物理地址表示(3,6),则物理地址为3*29+6 页 页内偏移=帧内偏移,但是通常页号和帧号不等 进程逻辑地址的表示(p,o)(页号,偏移量),得到的是虚拟地址 页到帧的映射,逻辑地址中的页号连续,物理地址中的帧号不连续,不是所有的页都有对应的帧 页表和段描述符表很像,稍有不同。段式是在描述符表中直接找到的基址,页式是在页表中找到的编号,编号要处理(也就是乘以2s)后得到像基址一样的地址。 下图是处理的过程:页号p-(页表)->帧号-(移位s+偏移量o)->物理地址空间
负责逻辑页号到物理页号的转换。 每个进程都有一个页表,随着进程运行状态而动态变化,根据页表基址寄存器(PTBR:Page Table Base Register)来查找 页表项组成 帧号f 页表项标志
存在位(resident bit)逻辑页号是否有一个物理帧与之对应修改位(dirty bit)内容是否修改引用位(clock/reference bit)是否有过对这个页面内容的引用性能问题: 每次访问一个内存单元时都要进行2次内存访问:先获取页表项,再访问数据 页表大小问题: 页表可能非常大,如果是32K物理内存,1K占一项,就有32项;64位机器每页1024个字节,就有254个页面,这个空间是非常大的。
解决方案: 缓存(Caching)快表 因为访问的地址可能是挨着的,所以可以把页表项缓存下来。 间接(Indirection)访问 多级页表 页表很大,切断,先找是在哪个子表中。
快表:近期访问过的页表项存入cpu中 这和计算机组成原理中的Cache很像
三级页号加偏移为例,大的页表分成若干段 可以把不存在的省掉 如图为二级页表的实现方式。
为了减少页表占用空间的做法。对于大地址空间的系统,多级页表变得很繁琐。比如5级页表,逻辑地址空间增长速度快于物理地址空间
页寄存器和反置页表的思路 不让页表和逻辑地址空间的大小相对应 让页表与物理空间的大小相对应
页寄存器 每个帧与一个页寄存器关联,寄存器内容包括 使用位(Residence)(此帧是否被使用) 占用页号(Occupier)(对应页号p) 保护位(Protection )(规定访问方式,可读or可写) 示例: 物理内存大小16MB,页面大小4KB,页帧数4K
段式和页式相结合 段存储在保护方面有优势,因为把同属性的数据或代码存放在同一个段中了; 页存储在内存利用和优化转移到后备存储方面有优势。 在段式基础上,在每个段加一级页表:段号+页号+物理偏移 通过指向相同的页表基址,实现进程间的段共享