【面试笔记】操作系统六

170 阅读10分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

具有快表的地址变换机构

局部性原理

分为:时间局部性和空间局部性

1、时间局部性:当前执行的某条指令、或者访问某个数据,在不久后可能再次被执行或者访问。

2、空间局部性:当程序访问了某个存储单元,在不久之后,其附近的存储单元可能被访问。

之前提到的基本地址变换机构中的“页表”,每次要访问一个逻辑地址时,都需要查询内存中的页表(每个进程有一个页表),由于局部性原理,可能每次都是访问的都是同一个页表项。

快表,称为:联想寄存器TLB,是一种访问速度比内存快很多的高速缓冲存储器(Cache),用来存放当前访问的若干页表项,以加快地址变换的过程。

内存管理的几种机制?

大体可分为:连续分配管理(分块)和非连续分配管理(页、段、段页)

  • 分块管理 属于连续分配管理机制,把内存分为几个大小相等且固定的块,每个进程占用其中一个,如果进程很小的话,会浪费大量的空间。已经淘汰。
  • 分页管理 把内存分为若干个很小的页面,相对比分块的划分力度更大一些。提高内存利用率。不会产生外部碎片(因为内存都按照统一页面大小进行划分),但会产生内部碎片(因为进程的最后一个页面往往小于页框(物理块)大小),页式管理通过页表对应逻辑地址和物理地址。 (页号和内存块号是一一映射的,映射的方法是通过页表)
  • 分段管理 把内存分为几个大小不定的有实际意义的段,比如 main 函数段,局部变量段,通过段表来把逻辑地址转为物理地址。
  • 段页式管理 结合了段式管理和页面管理的优点,把主存先分为若干个段,每个段又分为若干个页,段页式管理的段与段以及段的内部都是离散的。

分段

与分页最大的区别在于:离散分配时所分配地址空间的基本单位不同

将进程的地址空间,按照程序自身的逻辑关系,划分为若干个段,每段从0开始编址,以段为单位分配内存,每个段在内存中占据连续空间,但各段之间可以不相邻。

例如:段名:main存放main函数,段名:f存放f函数,等等

程序被划分为多个段,各段离散地装入内存,为了保证程序能正常运行,必须能从物理内存中找到各个逻辑段的存放位置,为此,就需要为每个进程建立一张段映射表,段表,段表由:段号、段长、基址构成,相比于页表,多了一个段长(因为页面大小都是一致的,但是段长不一定),但是段表项的长度是相同的。

进程的PCB中有段表寄存器,记录了该进程的段表的起始地址和段表长度

由于分页中每个页面大小相同,所以不需要对页内偏移量做越界检查;但是由于分段的每一个段大小不一定相同,所以需要将段内偏移量与段表中的段长做越界检查。

分页和分段的区别?

  • 共同点的话:

    • 都是离散分配的,单个页和单个段的内存空间是连续的。
    • 都是为了提高内存利用率,减少内存碎片。
  • 不同点:

    • 分页管理的页大小是固定的,由操作系统决定;分段管理的段是由用户程序所决定的。换句话说页对用户不可见,段对用户可见,用户编程时需要显示给出段名
    • 分页是为了满足操作系统内存管理的需求,页是没有实际的意义的;而段是有逻辑意义的,能够更好地满足用户需求,在程序中可认为是代码段、数据段。
    • 页的大小固定且由系统决定;段的长度不固定,取决于用户编写的程序。
    • 分页的用户进程地址空间是一维的(页面大小是一样的,可以直接算出页号和页内偏移量,靠着页号去找页表就行);而分段的用户进程地址空间是二维的(段的大小并不相同,不能直接算出段号和段内偏移量,所以需要给出段号和段内偏移量)
    • 分页的内存利用率高,不会产生外部碎片;而分段如果单段长度过大,为其分配很大的连续空间不方便,会产生外部碎片
    • 因为页面都是一样大的,内存都被分成了一个个物理块(页框),所以不会产生外部碎片,但是进程的最后一个页面可能没有物理块那么大,所以会产生内部碎片。

      而段不是一样大的,但段一定是进程的大小,按照段去分配内存,不会产生内部碎片,但由于段大小不同,会出现外部碎片

    • 分段比分页更容易实现信息的共享和保护! 因为段是按照逻辑功能划分,一个段往往是某个函数,让进程都来访问该段就可以很容易实现共享。

讲讲分页管理的快表和多级页表(按照why how的方式来回答,即为什么出现快表,是如何解决痛点的)

快表
  • why? 首先快表的引入是为了加快逻辑地址到物理地址的访问速度的,在引入快表之前,由逻辑地址访问到内存的过程是这样的: a) 首先根据逻辑地址的高位拿到页号 b) 根据页号访问内存中页表,根据页表的映射拿到实际的内存块号。(一次访问) c) 把内存块号和页内偏移量拼接,得到物理地址 d) 访问对应的内存物理地址。(二次访问)
  • 这样是需要有两次直接访问内存的过程的,所以为了加快这个速度,引入了快表
  • 快表,称为:联想寄存器TLB,是一种访问速度比内存快很多的高速缓冲存储器(Cache),用来存放当前访问的若干页表项,以加快地址变换的过程。
  • 对快表的访问速度远远快于访问内存中的页表

  • how? 根据局部性原理,被访问后的内存块很可能在短时间内再次被访问,程序在一段时间内可能会多次访问同一个页表项。
  • 所以,可以将当前访问的若干页表项放入快表,在每次访问页表项时,先在快表里查询是否有该页表项,如果没有再去页表中查询,并把查到的页表项放入快表。(如果快表满了,就根据一些策略把里面的页表项淘汰掉,再把新查询的页表加入进去)

使用快表后,根据给出的逻辑地址,计算出页号、页内偏移量(也就知道了页面大小、物理块大小),将页号与快表中的所有页号进行比较,如果找到匹配的页号,直接从快表中取出该物理块号,将物理块号(因为物理块大小已知)与页内偏移量拼接形成物理地址,最后访问该物理地址对应的内存单元即可。

若快表命中,访问某个逻辑地址只需要进行一次访存

如果没有命中,就需要两次访存,一次去内存中的页表(慢表)中找页号对应的物理块号,将页内偏移量和物理块号组成物理地址,第二次访存去找到对应的内存单元。(在找到页表项后,同时需要将其存入快表,以便后面可能的再次访问)

注意,快表没有命中,既需要先访问快表,还需要两次访问内存(这里默认先查快表,再查慢表)

多级页表(单级页表存在的问题)
  • why? 多级页表主要是为了解决页表在内存中占用连续空间太大的问题的,典型的时间换空间,因为多级页表增加了查询时间,但是减少了页表占用内存空间太大的问题。
  • how?讲个例子即可:在引入多级页表之前,我们使用单级页表来进行存储页表项,假如虚拟内存为 4GB,每个页大小为 4KB,那么需要的页表项就为 4GB / 4KB = 1M 个!每个页表项一般为 4B,那么就需要 4MB 的空间,大概需要占用 1000 个页来存页表项。 所以如果引入两级页表,让一级页表的每个页表项不再映射 4KB,而是映射 4MB,那么需要的一级页表项的个数为 4GB / 4MB = 1K 个,再让每个一级的页表项映射 1K 个二级页表项。当一级页表的某个页表项被用到时,再把该一级页表项对应的所有 1K 个二级页表项加载到内存中,这样可以节省大量的空间!

因为页表的存储需要占用一片连续的内存空间,由于页表项过多,导致页表的存储需要占用很大一片连续的内存空间,丧失了分页管理的离散存储优势。

同时,根据局部性原理可知,进程在一段时间内只需要访问某几个页面就可以正常运行,因此,没有必要让整个页表都常驻内存。

单级页表存在的问题:

1、页表必须连续存放,当页表很大时,需要占用很大一片连续的内存空间

2、没有必要让整个页表常驻内存,根据局部性原理,进程在一段时间可能只会访问几个特定的页面

解决方法:将页表进行分组,将各个分组离散的分配到各个内存块中,同样还需要对离散分配的页表再建立一张页表,称为:目录页表

解决问题一

1、按照地址结构将逻辑地址拆分成三部分:一级页号、二级页号、页内偏移量

2、从PCB中读出页目录表的起始地址,再根据一级页号查询页目录表,找到下一级页表在内存中的存放位置,也就是找到二级页表

3、用二级页号去二级页表中找到最终想访问的内存块号

解决问题二

在页表项中增加一个标志位,用于表示该页面是否已经调入内存中,若想要访问的页面不在内存中,则会产生缺页中断(内中断),然后将目标页面从外存调入内存。

采用多级页表机制,则各级页表的大小不能超过一个页面

一级页表有2^8个页表项,二级页表有2^10个页表项,三级页表有2^10个页表项,每个页表项的大小就是页面大小,所以2^8 x 2^10 x 2^10 x 2^12 = 2^40,刚好是40位逻辑地址。

两级页表需要三次访存,n级页表(不考虑快表)访问一个逻辑地址,需要n+1次访存。所以采用多级页表机制,会增加访存时间