Java教程

80386内存管理

本文主要是介绍80386内存管理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

这篇文章翻译自Intel 80386 Reference Programmer’s Manual的第5章。

文章目录

  • 第五章 内存管理
    • 5.1 段翻译(Segment Translation)
      • 5.1.1 描述符
      • 5.1.2 描述符表
      • 5.1.3 选择符
      • 5.1.4 段寄存器
    • 5.2 页翻译(Page Translation)
      • 5.2.1 页框(Page Frame)
      • 5.2.2 线性地址(Linear Address)
      • 5.2.3 页表( Page Tables)
      • 5.2.4 页表条目Page-Table Entries
        • 5.2.4.1 页框地址Page Frame Address
        • 5.2.4.2 Present Bit
        • 5.2.4.3 Accessed and Dirty Bits
        • 5.2.4.4 Read/Write and User/Supervisor Bits
      • 5.2.5 页翻译Page Translation Cache
    • 5.3 结合段翻译和页翻译
      • 5.3.1 "Flat" Architecture
      • 5.3.2 跨越多个页的段
      • 5.3.3跨多个段的页面
      • 5.3.4 非对齐页面和段边界Non-Aligned Page and Segment Boundaries
      • 5.3.5 对齐页面和段边界Aligned Page and Segment Boundaries
      • 5.3.6 每个段的页表Page-Table per Segment

第五章 内存管理

80386把逻辑地址(logical address,即程序员所看到的地址)翻译为物理地址(physical address,即物理内存中的实际地址),通过两步:

  • 段翻译(segment translation),把逻辑地址(由段选择符(segment selector)和段偏移(segment offset)组成)转化为线性地址(linear address)

  • 页翻译(page translation),将线性地址转换为物理地址。这一步是可选的,由系统软件设计人员决定。

这些翻译过程对于应用程序员(applications programmers)是不可见的,图 5-1 抽象的说明了这两个过程:

图 5-1 和本章接下来的部分简要说明了80386的寻址机制。但是实际上,寻址机制还包括内存保护的功能(memory protection features)。简单起见,这部分内容放在第6章。

5.1 段翻译(Segment Translation)

图 5-2 详细展示了处理器如何将逻辑地址转换成线性地址。

翻译需要用到下列数据结构:

  • 描述符(Descriptors)
  • 描述符表(Descriptor tables)
  • 选择符(Selectors)
  • 段寄存器(Segment Regsters)

5.1.1 描述符

段描述符(segment descriptor)给处理器提供其所需数据,以便它将逻辑地址映射为(map)线性地址。描述符由编译器(compilers),链接器(linkers),加载器(loaders)或者操作系统创建,而非应用程序员。图 5-3 说明了两种通用描述符的格式。所有类型的段描述符都采用这两种格式之一。

image-20220129172013408

段描述符的字段(fields)分别表示:

BASE:定义了段在4GB线性空间中的位置,处理器将基地址(base address)的三部分连在一起形成一个32位的值。

LIMIT:定义了段的大小(size),处理器将limit字段的两部分连在一起形成一个20位的值。处理器有两种翻译limit字段的方式,具体根据粒度位(granularity bit)的设置:

  1. 以一个字节(1B)为单位,定义上限不超过1MB。(In units of one byte, to define a limit of up to 1 megabyte.)
  2. 以4KB为单位,定义上限为4GB。加载时,limit字段左移12位,然后插入低阶1位。(The limit is shifted left by 12 bits when loaded, and low-order one-bits are inserted.)

Granularity bit:指定解释(interpret)limit字段的单位,当这一位被清零(clear),limit单位为1B;当这一位被置1(set),limit单位为4KB。

TYPE:描述符的类型。

DPL (Descriptor Privilege Level):在保护机制中被使用(见第6章)。

Segment-Present bit:如果这一位是0,描述符在地址转换中是无效的;当描述符(descriptor)的选择符(slector)被加载到段寄存器中时,处理器会抛出异常。图 5-4 说明了当present-bit为0时描述符的格式。操作系统可自由使用标记为AVAILABLE的位置。实现基于段的虚拟内存(virtual memory)的操作系统在下面情况之一时会清除present位。

  1. 段跨越的线性空间没有被分页机制映射。(When the linear space spanned by the segment is not mapped by the paging mechanism.)
  2. 当段不在内存中时。(When the segment is not present in memory.)

Accessed bit:当段被访问时,这一位被置1;即,描述符的选择符被加载到段寄存器里或者被一条选择符测试指令使用。在段级别(at the segment level)实现虚拟内存的操作系统可以通过定期测试和清除这个位来监视段的使用频率。

创建并维护描述符是系统软件的责任,需要编译器,程序加载器(program loaders)或者系统创建器(system builder),评分系统(the rating system)的合作。

5.1.2 描述符表

段描述符存在下面两种描述符表中:

  • 全局描述符表(The global descriptor table (GDT))
  • 一个局部描述符表(A local descriptor table (LDT))

一个描述符表是一个包含描述符的8字节条目的内存数组,如图 5-5。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x39QrpJs-1643519629652)(https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/20220129193131.png)]

描述符表长度可变,最多包含8192(2^(13))个描述符。GDT的第一个条目(INDEX=0)是不被处理器使用的。

处理器通过GDTR寄存器和LDTR寄存器来定位内存中的GDT和当前的LDT。这些寄存器存储表在线性地址中的的基地址并且存储段限制(segment limits)。指令LGDTSGDT访问GDTR;指令LLDTSLDT访问LDTR

5.1.3 选择符

逻辑地址的选择符部分(The selector portion of a logical address)通过指定(specify)描述符表并在该表中索引(index)描述符来标识(identify)描述符。选择符可以作为指针变量中的字段对于应用程序可见,但是选择符的值通常是由链接器或链接加载器(linking loaders)分配(assign,fixed up)的。图 5-6 显示了选择符的格式。

Index:从一个描述符表中的8192个描述符中选择一个。将Index的值乘以8(左移3位),将结果加到描述符表的基地址上,以此获得表中的相应描述符。

Table Indicator:表示选择符对应的描述符表。0表示GDT,1表示当前LDT

Requested Privilege Level:在保护机制中使用。(见第6章)

因为GDT的第一个条目是不使用的,一个index为0并且TI为0的选择符(即一个指向GDT第一项的选择符)可以被视为一个空(null)选择符。当一个段寄存器(CS或SS除外)被装入一个空选择符时,处理器不会引起异常。但是当使用段寄存器取访问内存时会有异常。这个特性用来初始化未使用的段寄存器,以便捕获意外引用(trap accidental references)。

5.1.4 段寄存器

80386把来自描述符的信息存在段寄存器里,从而避免了每次访问内存时都要查询描述符表。

每个段寄存器都有可见和不可见的部分,如图 5-7 所示。可见部分被程序操作,就像是16位寄存器。不可见部分由处理器操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nCyzRZit-1643519629654)(https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/20220129203154.png)]

这些寄存器由一般的程序指令操作(见第3章)。这些指令分为两类:

  1. 直接加载指令,例如MOV, POP, LDS, LSS, LGS, LFS,这些指令显式地引用段寄存器。
  2. 隐含(implied)加载指令,例如CALL 和 JMP,这些指令隐式地引用CS寄存器,并用一个新值加载它。

使用这些指令,一个程序使用一个16位的选择符加载段寄存器的可见部分,处理器自动获得基地址,limittype和其他来自描述符表的信息,并把它们加载到段寄存器的不可见部分。

因为大多数指令访问的段中的数据,其选择符已经被加载到了段寄存器里,所以处理器可以把由指令提供的段相对偏移(segment-relative offset)加到段基地址上,没有额外开销。

5.2 页翻译(Page Translation)

地址翻译第二阶段,80386把线性地址转化为物理地址。这个阶段的地址翻译实现了面向页面(page-oriented)的内存系统和页面级(page-level)保护所需的基本特性。

页翻译阶段是可选的。页翻译只有在当寄存器CR0PG位被置1时才生效。这个位通常由操作系统在软件初始化期间设置。如果操作系统要实现多个虚拟8086任务、面向页面的保护或面向页面的虚拟内存,则必须设置PG位。

5.2.1 页框(Page Frame)

一个页框是物理内存中的地址连续的4K个字节。页框从字节边界开始,并且大小固定。

5.2.2 线性地址(Linear Address)

线性地址通过指定页表、页表中的页以及页内的偏移量间接指向物理地址。图 5-8 显示了一个线性地址的格式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HtgstNX6-1643519629656)(https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/20220129213214.png)]

图 5-9 显示了处理器如何通过查询两级页表将线性地址中的 DIR, PAGE, 和 OFFSET字段转化为物理地址。寻址机制使用DIR字段作为页目录(page directory)的索引,使用PAGE字段作为由页目录确定的页表(page table)的索引,使用OFFSET寻址到一个由页表确定的页中的字节。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-msJrmq9g-1643519629657)(https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/20220129220924.png)]

5.2.3 页表( Page Tables)

页表是一个32位页说明符(page specifiers)的数组。页表本身就是一个页,因此包含4KB的内存,或者最多1K个32位的条目(entry)。

使用两级页表寻址一页内存。第一级是页目录。页目录最多可以寻址1K个第二级的页表。第二级页表最多可寻址1K个页。所有的表通过一个页目录寻址,因此,可以最多寻址1M个页(2^(20))。 因为每个页面包含4K字节(2 ^ (12)),所以一个页目录的表可以跨越80386的整个物理地址空间(2 ^ (20)乘以2 ^ (12)= 2^(32))。

当前页目录的地址存在寄存器CR3中,也成为页目录基址寄存器(page directory base register (PDBR))。内存管理软件可以选择使用每个任务(task)一个页目录,所有任务共用一个页目录,或者这两者的结合。CRR3的初始化见第10章,针对每个任务的切换见第7章。

5.2.4 页表条目Page-Table Entries

两个级别的页表中的条目具有相同的格式。图 5-10 说明了格式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FC0EDHso-1643519629659)(https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/20220129223309.png)]

5.2.4.1 页框地址Page Frame Address

页框地址指定了一个页的物理开始地址。因为页面位于4K边界上(译注:或者说4K字节对齐),所以低12位总是0。页目录中,页框地址是一个页表的地址,在二级页表中,页框地址是包含所需内存操作数的页框地址。

5.2.4.2 Present Bit

present bit指示了一个页表项是否可以在地址翻译中被使用。P=1表示可以使用。

在任何级别的页表中,如果P=0,则条目不能用在地址翻译,条目其他部分可以供软件使用;条目中的其他位都不会被硬件测试。图 5-11说明当P=0时页表项的格式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uSOFU5RH-1643519629660)(https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/20220129224330.png)]

任何一级的页表中,如果P=0,当尝试使用页表条目进行地址转换时,处理器会发出页面异常信号。在支持页式虚拟内存(paged virtual memory)的软件系统中,page-not-present异常处理程序可以把需要的页带到内存中。造成异常的指令可以重新执行。更多关于异常处理带到信息参阅第9章。

注意页目录本身没有present bit,当相应的任务被挂起时,页目录可能不存在,但是操作系统必须保证任务被分派(task dispatching)之前,TSSCR3所指示的页目录已经存在在物理内存中。关于TSS和任务分派的解释参阅第7章。

5.2.4.3 Accessed and Dirty Bits

这些位提供关于页的使用数据,两个级别的页表均如此。除了页目录条目中的dirty bit外,这些位都由硬件设置;然而,处理器并不清除这些位中的任何一个。

在对页面进行读写操作之前,处理器将页表中相应的访问位设置为1,两个级别的页表均如此。

在第二级页表中,对某个页表条目所覆盖的地址进行写操作之前,处理器将dirty bit设置为1。目录条目中的dirty bit未定义。

支持分页虚拟内存的操作系统可以使用这些位来确定当内存需求超过可用物理内存时,应该从物理内存中删除哪些页面。操作系统负责测试并且清除这些位。

阅读第11章,了解80386怎样在多处理器系统中更新acessed bitdirty bit

5.2.4.4 Read/Write and User/Supervisor Bits

这些位不用于地址转换,而是用于页级保护,处理器在地址转换的同时执行这些保护。第6章更相信的讨论了保护。

5.2.5 页翻译Page Translation Cache

为提高地址翻译效率,处理器将最近使用的页表数据存放在片上缓存中(on-chip cache)。只有当必要的页信息不在缓存上,这两级页表才都要访问。

页翻译缓存的存在对于应用层程序员是不可见的,但对于系统层程序员不是如此。当页表改变时,操作系统程序员必须刷新缓存。页翻译缓存可以使用下面两种方法之一刷新:

  1. 通过使用一个MOV指令重新加载CR3,例如:

    MOV CR3 EAX
    
  2. 通过任务切换(task switch)到一个拥有和当前TSS不同的CR3映像(image)的TSS。(关于任务切换的更多信息见第7章)

5.3 结合段翻译和页翻译

图5-12结合了图5-2和图5-9,总结了当分页启用(enable)时,从逻辑地址转化为物理地址的所有阶段。通过对两个阶段的选项和参数的适当选择,内存管理软件可以实现几种不同类型的内存管理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ffwy1G9x-1643519629661)(https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/20220130014715.png)]

5.3.1 “Flat” Architecture

当80386被用来执行为没有段的架构(architecture)设计的软件时,“关闭”(turn off)80386的段特性可能是权宜之计。80386没有禁用(disable)段的模式,但是同样的效果可以通过初始把包含整个32位线性地址空间的描述符的选择符加载到段寄存器来实现(but the same effect can be achieved by initially loading the segment registers with selectors for descriptors that encompass the entire 32-bit linear address space)。80386指令使用的32位偏移量足以对整个线性地址空间寻址。

5.3.2 跨越多个页的段

80386架构允许段比一个页(4KB)大或者小。例如一个段被用来对一个132KB的大数据结构寻址和保护。在一个支持分页虚拟内存的软件系统中,不需要把整个数据结构同时放在物理内存中。这个数据结构被分为33个页,任何一个页都可能不在内存中。应用程序程序员不需要知道虚拟内存子系统以这种方式对结构进行分页。

5.3.3跨多个段的页面

另一方面,段可能小于页的大小。例如,考虑一个类似于信号量的小数据结构。因为段提供保护和共享,对于每个信号量建立一个分离的段是有用的。但是一个系统可能需要多个信号量,每个分一个页是低效率的。因此,可以把很多相关的段聚集在一个页里。

5.3.4 非对齐页面和段边界Non-Aligned Page and Segment Boundaries

80386架构不强制要求页面边界和段边界的对应关系。页可以包含一个段的结束和另一个段的开始。同样的,段可以包含一个页的开始和另一个页的结束。

5.3.5 对齐页面和段边界Aligned Page and Segment Boundaries

如果强制要求页边界和段边界对齐,内存管理软件可以更简单。例如,如果段只以一页为单位进行分配,段和页分配的逻辑可以结合在一起。(if segments are allocated only in units of one page, the logic for segment and page allocation can be combined)不需要逻辑解释部分使用的页。

5.3.6 每个段的页表Page-Table per Segment

一种可以进一步简化空间管理软件的空间管理方法是在段描述符和页目录条目之间维护一对一的关系,如图5-13。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UBOTJ8Ts-1643519629663)(https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/20220130112827.png)]

每个描述符有一个基址,低22位为0;用另一句话说,基址由页表的第一个条目映射。(?)(Each descriptor has a base address in which the low-order 22 bits are zero; in other words, the base address is mapped by the first entry of a page table.)

段可有1到4MB的限制。根据限制,段包含在1到1K个页框之中。因此,一个任务被限制为1K个段(对于许多应用程序来说已经足够了),每个段最多包含4MB。描述符、相应的页目录条目及页表,可以同时同时被分配和释放。

这篇关于80386内存管理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!