OS concept memory management

Posted by Taolee on October 20, 2018
  1. MMU的作用与演化 在一个多进程的系统中,由于各个进程需要共享同一个物理内存,但是并不共享逻辑内存。所以需要由硬件和操作系统来完成,内存的管理,使得进程在能够共享同一个物理内存的基础上能够有独立的地址空间,不被别的进程影响。 MMU:CPU里面的一种专用硬件,负责将进程的逻辑地址转换成实际的物理地址。MMU需要与操作系统配合来完成内存的管理。例如:在一个多进程的操作系统中,操作系统进行进程的切换时,必须保存换出进程的MMU信息(比如,页表基址寄存器的值等)。

在出现复杂的MMU之前,CPU中可以只用地址基址寄存器(下称base)和地址偏移量寄存器(下称offset)来将当前进程的逻辑地址转换成物理地址。操作系统保存每一个进程的base/offset的值,在context switch时,保存换出进程的base/offset值,将换入进程的base/offset值设置到相应的CPU寄存器中。当进程p需要访问地址a时,CPU将a翻译为物理地址:base+a, 并且确保a没有超出offset。 但是上述方案的缺点是:如果将物理地址空间划为相同的块,那么进程数量将受限于物理地址空间的分块数量。块不能划的太大,也已不能划的太小。如果将物理地址空间划成大小不同的块,会出现一定的内存碎片,当一个进程需要特定的内存大小时,常常面临着,及时内存中的空洞加起来足够,但是进程仍然无足够块可用的局面。问题的根源在于:划分不够灵活,且难以真正共享。

页表方案:

将逻辑空间划分为页,将物理内存划分为帧,页的大小与帧的大小完全相同。一个页对应一个帧。每一个进程有一个自己的查找表,完成从页到帧的查找。 进程的逻辑地址分为两部分:页码 + 页偏移, 逻辑地址映射为物理地址:帧码+页偏移。

  • 不同的进程可以共享完全相同的物理帧(比如可以用于:动态链接库的共享,基于共享内存的进程间通信)
  • 逻辑上相邻的页,在物理内存中可以在任何位置,有利于提升物理内存的利用率,减少外部碎片。
  • 一个进程的某一个页(逻辑),只有在使用时才为其分配帧(物理),完成分配的过程包括:将这个页->帧 映射的条目写入页表,同时将数据/代码加载进有帧指定的物理地址。
  • 操作系统必须保留每一个进程的页表信息,方便在context switch的时候加载页表信息到MMU。
  • 操作系统必须保留系统中帧的分配信息:即每一帧的使用情况,每一帧由那一个进程使用,以能够在进程请求访问页的时候为其分配可用帧。

进化: 对于较为古老的系统(CPU+OS),由于所拥有的内存较小。所以页表也较小,必须对于16位寻址的系统,假设每一个的大小为1024byte(10位bit页偏移),那么页码使用6bit表示,也就是64个页表条目,可以完全使用寄存器来存储页表。 但是对于现代系统,假设是32位地址空间的话,假设页大小为1mb(20bit页偏移),那么页码需要30bit表示,页表的条目大小时:2^12=4096项,使用CPU里面的寄存器来存储整个页表太过昂贵,如果对于更小的页和更大的地址空间,将页表完全存储的CPU里面的寄存器已经不太可能。即使可以完全的将页表存储在CPU内部。切换进程(context switch)的时候要将整个换出进程的页表从寄存器换到内存,再将换入进程的整个页表从内存加载进CPU页表寄存器组的时间消耗也太过昂贵。 为此现代系统通常不将整个页表保存在CPU中,而是将完整的页表存储在内存中,每一个进程只关联一个指向页表起始地址(物理地址)的页表指针,进程切换时,只需要将页表指针的值放入页表基址寄存器。 查找某一页时,先将页码+页表基址寄存器,从内存页表中读取帧的值。

但是上述方案的问题是:CPU的每一个内存读或写操作(即每完成一个逻辑地址到物理地址的转换),都需要额外的读一次内存,即从内存中查找页表。这大大降低了系统的性能。为了克服这个缺点。现代系统中根据cache的理念,设计了专用于缓存页表条目的cache,即TLB。进程的每一个页查找(页码->帧码)都先经过TLB,如果该页表条目在TLB当中,则直接将TLB中的帧码返回。如果没在TLB中,再根据页表基址寄存器去内存中查找页表,并将查找到的页表条目存储于TLB中。