操作系统------内存管理

概念:

CPU所生成的地址通常称为逻辑地址(编译程序时生成的地址);

内存单元所看到的地址通常称为物理地址 ;

重定位:修改程序中的地址(逻辑地址)到物理地址;

什么时候完成重定位?

  1. 编译时可完成重定位,缺点是在编译时需要知道内存哪一段是空闲的, 但是在实际的操作系统是没法知道。如在编译时内存1000是空闲的,但是在执行时,可能1000已经被其它进行所用。所以如果在编译时重定位的程序只能放在内存固定位置。
  2. 载入时可完成重定位,相比编译时的重定位更加的灵活,根据当前内存的哪一段是空闲的,找到其基址,在载入程序时,程序中所有的逻辑地址加上基址即可。载入时重定位的程序一旦载入内存就不能动了。需要修改地址,慢一些。
  3. 运行时重定位,一个程序和进程是载入后,在内存也会发生移动,所以载入时重定位就不能满足要求,运行时重定位是每执行一条指令都要从逻辑地址算出物理地址,如下图:

交换:进程的换入和换出

让程序执行起来:

1.创建进程,创建PCB

2.在内存中找一段空闲的内存,找到其基址,赋给PCB,同时将程序放置在内存中

3.置好PC值,将PCB中的基址放置在基址寄存器中

4.进行地址翻译,PC再取址执行

在实际的操作系统中并不是将整个程序一起载入内存,而是实际的程序由若干部分(段)组成的,如下图所示:

每个段有各自的特点,用途。程序编译完后,不是所有程序都是从0开始,而是每一部分(段)都是从0开始,如下图:

如何定位具体指令(数据):<段号, 段内偏移> 如mov[es:bx], ax

程序为何需要这样设计呢?符合用户观点,可以独立考虑每个段(分治),

如,程序段的特点是只读,而数据段是可写的,如果将程序段和数据段放在一起处理,在操作数据时,可能由于偏差导致程序段改变。如果是不放在一起处理,由于程序段只读的特点,无论怎么写也不会改变程序段。

再如堆栈段是可增长的,如果集中处理的问题是如下图所示:

如果第3是堆栈段,当3 堆栈段增长后,超过了1的界线(堆栈段预留的空间不够),因为如果是集中处理,则需要重新申请一段内存,将所有的0,1,2,3全都复制到新的内存中,如果是分段处理,则只需要移动3堆栈段即可,可以做到效率上的提升和内存的充分利用。

使用分段后,寻址的方式将是<段号, 段内偏移>,每段的基址不一样,所以需要有一个表来记录每个段的基址,即段表,段表如下图所示:

GDT表:操作系统OS对应的段表;

LDT表:进程对应的段表,每个进程都有一个LDT表;

让程序执行起来:

1.创建进程,创建PCB

2.在内存中找空闲的内存放置程序的各个段,并将每段的段基址放入进程的LDT表中

3.将LDT表赋给PCB,同时将LDT表的数据ldtr(段表寄存器),

4.置好PC值,根据ldtr表进行地址翻译,PC再取址执行

三步:

1.将程序分成多个段(编译做的事)

2.在内存中划分空闲空间

3.通过磁盘读写将程序载入到内存

内存怎么割?

固定分区:等分,操作系统初始化时将内存等分成k个分区,在实际操作系统中不合适

可变分区:根据段的大小分配内存。其核心的数据结构是有两个表,空闲分区表,已分配分区表,如下图:

对于下面一个空闲分区表,有一个段提出内存请求:reqSize=40K,应该选择哪一个合适呢?

没有具体的答案,根据需要算法做出选择

最佳适配:选取最接近目标大小的分区,特点是最后的空闲分区会越来越细

最差适配:选取离目标大小最大的分区,特点是最后的空闲分区比较均匀

首先适配:找到的第一个适合的分区,特点是表查的快

在实际的操作系统中可变分区造成的问题:

为解决这个问题,操作系统将内存分成页,针对每个段内存请求,系统一页一页的分配给这个段,一个进程最大的内存浪费是一页,不需要内存紧缩。

对于每段的被分配到了哪一页上呢?主要根据页表,页表对就的寄存器为cr3,如:

如何计算0x2240对应的物理地址呢?假设页面大小为4K,页号=0x2240/4K的除数,即0x2240向右移动三位,页号为2,偏移量为0母40,在页表中根据页号查找对应的页框号3,页框号3对应的物理地址为3*4K,最终的物理地址为0x3000+0x240=0x3240。这一过程由MMU(内存管理单元)来完成。

为了提高内存空间利用率,页应该小,但是页小了页表就大了。对就4G的内存,如果页大小的4K,则页表共有4G/4K=1M页面。页表很大,页表的放置就成了问题,每个页面存放需要4Byte,1M页面则需要4M内存,而每个进程都有自己的页表,如果系统中有100个进程,就需要400M内存。这很显然是一种内存浪费。实际上大部分逻辑地址根本不会用到,对不使用的逻辑页号出页表中删除,减小页表的大小。

第一种尝试,只存放用到的项,如下图:

但页表中的页号不连续,就需要比较、查找,折半log(2^20)=20,20次额外的内存访问。导致指令执行的速度降低到了原来的1/20。因此这种方式不可取。页号必须连续,只需要一次就可以找到对应的页号,但是大页表占用内存,造成浪费,如何使页号连续而又让页表占用内存少呢?多级页表解决这个问题。 如下图:

多级页表提高了空间效率,但在时间上呢?每多一级页表就多一个额外的访问内存。对于32bit系统额外多3次访问内存,对于 64bit系统额外多6次访问内存。这个问题如何解决呢?使用快表TLB, 是一组相联快速存储,是寄存器。如下图:

真正的内存管理:段页结合

程序员希望用段,物理内存希望用页,所以两种机制要结合。 如下图:

猜你喜欢

转载自blog.csdn.net/qq_34809033/article/details/90208801