操作系统-内存管理机制

本文总结了我对内存管理机制的学习,先从通用的操作系统课本的内存管理机制学习,然后深入到Linux操作系统,对于内存管理机制的实现。

通用操作系统内存管理机制

这一目我主要参考的是汤小丹老师《计算机操作系统》当中的内容,在此先说明。

概述

  1. 课本上并非直接讲内存管理,而是即cpu管理讲完之后,开始的存储器管理。毕竟它是操作系统需要负责进行管理的,当然这里面的重中之重当然是对于内存的管理。
  2. 内存管理当中最重要的就是,将程序装入内存的时候,需要给用户程序在内存中分配内存。这也是程序和进程的主要区别,进程是要占资源的。
  3. 在给用户程序在分配内存的时候,有两种方式,一种是为用户程序分配一个连续的内存空间。与之对应的是将用户程序存储在内存当中不连续的空间,也就是说为用户程序分配离散的内存空间。
  4. 程序装入的时候需要分配内存,有连续的方式,有离散的方式。离散的方式就是将用户程序分配在内存当中不连续的空间,主要有分页和分段两种机制。

分页机制

  1. 分页存储管理是将用户程序的逻辑地址空间分成若干个大小相同的片,称为页或者页面。相应的把机器的内存空间也分成大小相同的若干块,称为frame。page以及frame都是进行编号的。在为程序分配内存的时候,以块为单位,将用户程序当中的若干个页,装入到内存当中不相邻的物理快当中。,从而实现了,离散存储。
  2. 显然的是,既然你的逻辑地址空间要转化为物理地址空间,那么需要给出逻辑地址到物理地址的转换机制。通常情况下,对于一个32位的地址,给出页号所占的位数,那么剩下的地址位数就是页偏移。通常情况下,页的大小为4K.
  3. 此时,地址转换机制还需要页表的支持。当我们在用户程序拿到一个逻辑地址之后,首先得到页号,然后根据页表,查找其在内存中所存储的块号。然后,由于页的大小和块的大小一致,此时在加上之前的页偏移即可。
  4. 下图是地址变换机构,图片来自汤小丹老师的《计算机操作系统-第三版》 操作系统-内存管理机制
  5. 需要注意的是,页表通常不存储在寄存器当中。页表的起始地址和长度存在进程的pcb当中,当进程需要执行的时候,加载到寄存器当中,然后进行访存。所以,每次访存需要两次访问内存。一次获取页表,一次获取实际操作数。
  6. 页表其实相当于真是物理地址的索引,但是可能出现页表过长的情形,此时,我们可以再增加一层页表,将页表的内容存储在离散的空间里面。形成二级页表,从而第一级页表存储的是第二级页表的位置,后者存储的是真实的物理地址。图片来自汤小丹老师的《计算机操作系统-第三版》 操作系统-内存管理机制
  7. 多级页表对页表又进行了离散存储,虽然解决了大页表无需大片存储空间的问题。但是,并为解决用较少的空间去存储大页表的问题,换言之,用离散的方式存储页表并没有解决页表所占用的内存空间。
  8. 解决办法:

解决方法是把当前需要的一批页表项调入内存,以后再根据
需要陆续调入。在采用两级页表结构的情况下,对于正在运行的进程,必须将其外层页表
调入内存,而对页表则只需调入一页或几页。为了表征某页的页表是否已经调入内存,还
应在外层页表项中增设一个状态位 S,其值若为 0,表示该页表分页尚未调入内存;否则,
说明其分页已在内存中。进程运行时,地址变换机构根据逻辑地址中的 P 1 ,去查找外层页
表;若所找到的页表项中的状态位为 0,则产生一中断信号,请求 OS 将该页表分页调入内存。

分段机制

  1. 分页机制提高了内存的利用率,分段机制则是从程序员的角度出发,分页机制当中的页只是存放信息的物理块,并无完成的意义。然而段却是信息的逻辑单位。比如代码段,数据段,内存的布局也正是这么干的。(我个人感觉用户空间的进程内存布局就是段机制实现的,但是linux实际上没有分段机制,需要进一步讨论)
  2. 在分段存储管理机制当中,程序的逻辑地址空间被划分为若干个段(逻辑信息单位),每个段定义各自的逻辑信息。每个段定义了一组逻辑信息。例如,有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。
  3. 每个段都从0开始编址,并采用一段连续的逻辑地址空间。各个段的长度由相应的逻辑信息来决定,因此不同的段具有不同的段长。因此,程序的逻辑地址空间分成多个段。所以,对比分页,逻辑地址空间分成若干页,分段是逻辑地址空间分成若干段。
  4. 逻辑地址由段号和段内地址给出。不同段长度不同,因此段内地址也不同。
  5. 地址映射的实现。在分段式管理机制中,为每个段在内存中分配连续的内存空间,而各个段则存储在内存的离散空间当中。这点和分页的机制是一样的,分页是每个也在内存中存储在不连续的空间当中,但是页内是连续的。同理,分段也是,每个段储存在内存离散的空间当中,但是段内是连续的。
  6. os对分段机制的支持。os为了支持分段机制,需要和分页机制一样,增加段表。逻辑地址空间的每个段在段表中占有一项,记录了逻辑段在内存空间的基地址以及段长(必须有段长,不然我怎么知道你越美越界,因为段的长度是不固定的)。
    操作系统-内存管理机制
    操作系统-内存管理机制

分页和分段的区别

由上所述不难看出,分页和分段系统有许多相似之处。比如,两者都采用离散分配方
式,且都要通过地址映射机构来实现地址变换。但在概念上两者完全不同,主要表现在下
述三个方面:
(1) 页是信息的物理单位,分页是为实现离散分配方式,以消减内存的外零头,提高内存的利用率。或者说,分页仅仅是由于系统管理的需要而不是用户的需要。段则是信息的逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了能更好地满足用户的需要。
(2) 页的大小固定且由系统决定,由系统把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的,因而在系统中只能有一种大小的页面;而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时,根据信息的性质来划分。
(3) 分页的作业地址空间是一维的,即单一的线性地址空间,程序员只利用一个记忆符,即可表示一个地址;而分段的作业地址空间则是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址。

课本上给了一个例子我觉得讲的非常好,把分段与分页的区别讲的很清楚,就是,段是逻辑信息的单位。在程序中可以体现为代码段,数据段等。考虑如下的情形:

例如,有一个多用户系统,可同时接纳 40 个用户,他们都执行一个文本编辑程序(Text Editor)。如果文本编辑程序有 160 KB 的代码和另外 40 KB 的数据区, 则总共需有 8 MB 的内存空间来支持 40 个用户。如果 160 KB 的代码是可重入的(Reentrant),则无论是在分页系统还是在分段系统中,该代码都能被共享,在内存中只需保留一份文本编辑程序的副本,此时所需的内存空间仅为1760 KB(40×40+160),而不是 8000 KB。

  • 分页机制实现共享
    为实现代码的共享,应在每个进程的页表中都
    建立 40 个页表项,它们的物理块号都是 21 # ~60 # 。在每个进程的页表中,还须为自己的数据区建立页表项,它们的物理块号分别是 61 # ~#70 71 # ~80 # 、81 # ~90 # ,…,等等。图 4-19是分页系统*享 editor 的示意图。操作系统-内存管理机制
    注意,看右侧的内存分布,代码段从#21-#60,这段代码是共享的。但是,对于每一个进程而言,他们都需要存储各自的页表,因为不同的进程具有独立的调度资源单位和内存分配单位。所以,每个进程都u型要存储40个页表项目(指向代码),以及10个页表项目(指向数据)

  • 分段机制实现共享
    对于分段机制则简单的多,每个进程只需存两个表项,一个段表表项指向代码段,一个指向数据段。对于分页和分段机制实现共享,内存中真实的数据确实只有一份。是共享的,但是页表和段表的数目不同。

段页式机制

  1. 段页式机制的原理是结合分段机制和分页机制,首先将逻辑地址空间按照逻辑单位的划分,划分为若干段。这段和分段机制是一样的,但是不同的是,分段机制内部是连续的。段页式是在段的内部进行了页式管理的机制,也就是说,之前段与段在内存当中是离散的,但是段内是连续的,现在段内也是离散的,段内用页式机制进行管理。
  2. 此时,用户的逻辑地址由三部分组成。分别是,段号,段内页号,页内地址。
  3. 地址变换过程:在段页式系统中,为了便于实现地址变换,须配置一个段表寄存器,其中存放段表始址和段表长 TL。进行地址变换时,首先利用段号 S,将它与段表长 TL 进行比较。若 S < TL,表示未越界,于是利用段表始址和段号来求出该段所对应的段表项在段表中的位置。在单纯的分段机制中,这点一样,在段表中,首先判断短号是否越界,然后,如果没有越界,根绝段号,找到段表中这一项,最后,拿到该段所对应的物理内存的起始地址。但是,由于段页式,逻辑段在内存中并不是连续存储的,此时就没有什么段在内存中起始地址的概念。此时,段表项中,存储的不是 [段号-内存地址],而是[段号-页号]。此时,os在根据页号,在这个段的页表当中,检索相应的[页号-内存地址]项目,从而此时才能去访问相应的块。
    操作系统-内存管理机制

虚拟存储器

  1. 定义:虚拟存储器技术是指从逻辑上对内存容量进行扩充的一种技术。
  2. 基于局部性原理,应用程序在运行之前,没有必要全部装入内存,仅须将那些当前要运行的少数页面或段先装入内存便可运行,其余部分暂留在盘上。程序在运行时,如果它所要访问的页(段)已调入内存,便可继续执行下去;但如果程序所要访问的页(段)尚未调入内存(称为缺页或缺段),此时程序应利用 OS 所提供的请求调页(段)功能,将它们调入内存,
    以使进程能继续执行下去。如果此时内存已满,无法再装入新的页(段), 则还须再利用页(段),的置换功能,将内存中暂时不用的页(段)调至盘上,腾出足够的内存空间后,再将要访问的页(段)调入内存,使程序继续执行下去。这样,便可使一个大的用户程序能在较小的内存空间中运行;也可在内存中同时装入更多的进程使它们并发执行。但是,对于用户而言,看到的是整个的存储空间,它并不知道os在进行请求分页以及置换,所以从用户角度而言,看到的就是一个虚拟存储器。
  3. 由上所述可以得知,所谓虚拟存储器,是指具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的一种存储器系统。
  4. 对于虚拟存储器而言,既然开始并不装入整个的程序,而是装入一部分,那么刚开始应该分配多少物理快呢?这就是内存分配的策略。