大话Linux操作系统

  本文将对Intel 32位系统架构中涉及的一些最基本的概念做概要介绍,在以后的博文中做具体阐述。如图1为Intel 32位系统架构总览。


图1

系统架构总览

全局描述符表(GDT)和局部描述符表(LDT)

       在保护模式下,所有内存访问都是通过全局描述符表(Global Descriptor Tables)或者局部描述符表(LocalDescriptor Tables)定位的,如图1所示。表的成员为段描述符(segment descriptors),段描述提供段基地址、段限长、段属性(段访问权限)、段类型和其它信息。每一个段描述符都有一个段选择符(segment select)与之对应,一个段选择符提供一个GDT或IDT的索引、一个指向标志TI(决定段选择符指向GDT还是IDT)和访问权限信息(请求特权级RPL)。

         为了访问段中的一个字节,必需提供一个段选择符和偏移值,段选择符提供了一种由段描述符到段访问的方法,处理器从段描述符中获取线性空间中的段基地址,段基地址再加上偏移值就确定了字节所在位置。访问数据段、代码段和堆栈段都是使用这种机制,提供的段必须是CPU的CPL(current privilege level)所接受的(CPL是当起可执行的代码段的保护级别)。

         GDT的线性基地址存储在GDT寄存器(GDTR)中;LDT的线性基地址存储在LDT寄存器(LDTR)中。

系统段、段描述符和门

       除了代码段、数据段和堆栈段是构成程序运行的环境外,系统架构还定义了两个系统段:任务状态段(task-state segement)和LDT。GDT不认为是段,因为它不通过段选择符或者段描述符访问。TSSs和LDTs都有相应的段描述符定义它们。

         系统架构还定义了一个特别的描述符集,称为门:调用门(call gates)、中断门(interrupt gates)、陷阱门(trap gates)和任务门(task gates)。这些门为运行在不同特权级的系统程序和处理程序提供保护。例如,一个对调用门的调用可以提供与当前代码段特权级相同或者数字更低(特权更高)的程序代码段的访问,也就是说程序的代码段特权级大于等于当前代码段特权级才可以访问。为了通过调用门访问程序,调用程序必需提供调用门选择符,然后由处理器通过调用门完成一个访问权限的检查——通过比较调用门的当前特权级和调用门指向的目标代码段的特权级。假如允许访问目标代码段,处理器从调用门中获取目标代码段的段选择符和段偏移。如果调用需要改变特权级,处理器也切换到目标特权级堆栈中。新栈的段选择符由当前运行的特务的TSS中获得。调用门使得在16位和32位代码段中转换变得更容易。

任务状态段(TSS)和任务门(taskgates)

  TSS定义了任务执行环境的状态。这些状态包括通用寄存器,段寄存器,标志寄存器,EIP寄存器以及三个堆栈段的段选择符(三个特权级0,2,3各一个)。TSS还包括一个LDT的段选择符,这个LDT把当前任务和页表基地址联系起来。

         保护模式下所有程序运行在任务上下文中(称为当前任务)。当前任务的TSS段选择符存放在任务寄存器(task register)中,最简单切换任务的方法就是通过call或jump 指令到新任务。这里新任务的TSS段选择符由call或jump指令提供。在任务切换过程中,处理器完成以下操作:

1.        在当前TSS中存储当前任务的状态。

2.        加载带有新任务段选择符的任务寄存器。

3.        通过GDT中段描述符访问新TSS。

4.        从新TSS加载新任务的状态到通过寄存器、段寄存器、LDTR、控制寄存器CR3(页表基地址)、EFLAGS标志寄存器和EIP寄存器中。

5.        开始执行新任务。

  任务也可以通过任务门访问。任务门和调用门类似,只不前者是对TSS的访问(通过段选择符),后者是对代码段的访问。

中断和异常处理

  外部中断、软中断和异常是通过IDT处理的。IDT存储提供访问中断和异常处理程序的门描述符的集合。和GDT类似,IDT也不是段。IDT的线性基地址存储在IDTR(中断门描述符表寄存器)中。

  IDT中的门描述符可分为:中断门、陷阱门和任务门。为了访问中断或异常处理程序,处理器首先接收一个中断向量(中断号),中断向量可以由内部硬件、外部中断控制器或者软件指令(比如INT,INTO,INT 3,BOUND)获得。中断向量提供IDT表的一个索引。假如选择的门描述符是中断门或陷阱门,相关的处理程序访问非常类似于通过调用门调用程序;假如选择的门描述符是任务门,处理程序是通过任务切换访问的。

内存管理

    系统架构支持直接物理寻址和虚拟内存(通过分页实现)。当使用直接物理地址时,一个线性地址就是一个物理地址;当使用分页时,所有的代码段、堆栈段、和系统段(包括GDT和IDT)可以将最近访问的页驻留在内存而分页(这句翻不太来)。物理内存中的页(有时称为页框)包含两种类型的系统数据结构:页目录和页表。两种数据结构都在物理内存中(见图1)。页目录的物理基地址存储在操作寄存器CR3中。一条页目录包含页表的物理基地址,访问权限和内存管理信息。一条页表包含页框的物理基地址,访问权限和内存管理信息。

         为了使用这种分页机制,一个线性地址被分为三部分:页目录,页表和页框。系统可以有一个或者多个页目录。例如,每个任务可以有它自己的页目录。

系统寄存器

         为了帮助处理器初始化和控制系统操作,系统架构提供存储系统标志的EFLAGS寄存器和几个系统寄存器:

l  系统标志和IOPL域在EFLAGS寄存器中,控制任务和运行模式切换、中断处理、指令跟踪他访问权限。

l  控制寄存器(CR0、CR2、CR3和CR4)包含多种标志和数据域来控制系统级操作。这些寄存器中的其它标志表示支持在操作系统和执行时的特别的处理能力。

l  debug调试寄存器允许在调试程序和系统软件时设置中断。

l  GDTR、LDTR和IDTR寄存器包括他们各自表的线性基地址和表的大小。

l  任务寄存器包含当前任务的TSS线性基地址和大小。

l  特别的模型寄存器。

其它系统资源

         除了在前面提到的系统寄存器和数据结构外,系统架构还提供以下额外的资源:

l  操作系统指令。比如LGDT、SGDT用来操作GDTR寄存器。

l  性能监控计数器。

l  内部高速缓存和缓冲区。

         性能监控计数器是用来记录处理器事件的事件计数器,比如指令解码数、中断接收数和高速缓存加载数。

         处理器提供一些内部高速缓存和缓冲区。高速缓存用来存储数据和指令,缓冲区用来存储类似于系统解码地址和应用程序段和等待执行的写操作。



运行模式

         IA-32架构支持四种模式。

l  保护模式(Protected mode)这是处理器主要运行模式。在这种模式下可以提供最多的架构特性指令集,最高的性能以及向后兼容。

l  实地址模式(Real-address mode) 这种模式提供了8086处理器程序运行的环境,比如提供切换到保护模式和系统管理模式的能力。

l  系统管理模式(System management mode SMM)从Intel 386 SLCPU开始,SMM就成为所有IA-32 CPU的标准架构特性。这种模式为操作系统实现电源管理和OEM特性提供透明机制。SMM是通过外部系统中断pin(SMI#)激活的,系统中断pin 产生一个系统管理中断(SMI)。在SMM模式下,处理器通过保存当前运行程序或任务的上下文切换到单独的地址空间。然后特别的SMM代码透明的执行。当从SMM返回时,处理器把SMI之前的状态返回。

l  虚拟8086模式(Virtual8086 mode) 在保护模式下,处理器支持一个准操作模式模式叫虚拟8086模式。在这种模式下允许处理器在保护模式和多任务环境下执行8086程序。

如图2显示处理器在不同模式之前切换。

         系统加载或复位时CPU处于实模式。CR0寄存器的PE标志用于控制CPU工作在实模式或保护模式。

         EFLAGS寄存的VM标志用于决定CPU工作在保护模式还是虚拟8086模式。保护模式和虚拟8086之前的切换通常完成任务切换或者从中断或异常处理程序返回的一部分。

    每当处理器接收到SMI,就会从其它模式切换到SMM。当执行RSM指令时,处理器总是返回到SMI发生之前的模式。

图2

EFLAGS寄存器中的系统标志和域

   EFLAGS寄存器中的c系统标志和IOPL域控制I/O,可屏蔽硬件中断,调试,任务切换和虚拟8086模式,如图3所示。只有特权级代码(通常为操作系统代码)允许修改这些标志位。


图3

内存管理寄存器

    处理器提供四个内存管理寄存器(GDTR,LDTR,IDTR和TR),用于指定分段内存管理所使用的系统表的基地址,如图4所示。处理器为这些寄存的加载和存储提供了特别的指针。


图4

GDTR全局描述符表寄存器

    GDTR寄存器保存了GDT的32位线性基地址和16位表长度。基地址指定GDT中字节0在线性地址空间的地址;表长度指明GDT表的字节长度。

    LGDT和SGDT指令分别用来加载和存储GDTR寄存器。在CPU刚加电或复位时,线性基地址默认设为0,表长度设置为0FFFFH。在保护模式CPU初始化过程中,GDTR寄存器必须加载一个新值。

LDTR局部描述符表寄存器

  LDTR寄存器保存了LDT的32位基地址,16位段选择符,限长和描述符属性。基地址指定LDT段中字节0在线性地址空间的地址;段限长指明LDT段的字节长度。

    LLDT和SLDT指令分别用来加载和存储LDTR寄存器。GDT表中必须包含LDT的段段描述符。当通过LLDT指令加载段描述符到LDTR时:LDT的基地址,段限长和描述符表属性将自动的加载到LDTR。

    当发生任务切换时,新任务LDT的段选择符和段描述符自动的加载到LDTR中。在写入新的LDT信息到LDTR之前,LDTR不会自动保存旧的信息。

    当处理器刚加电或复位时,段选择符和基地址被默认设为0,段限长默认高为0FFFFH。

IDTR中断描述符表寄存器

   IDTR寄存器保存了IDT的32位线性基地址和16位表长度。基地址指定IDT中字节0在线性地址空间的地址;表长度指明IDT表的字节长度。LIDT和SIDT指令分别用来加载和存储IDTR寄存器。在CPU刚加电或复位时,线性基地址默认设为0,表长度设置为0FFFFH。在CPU初始化过程中,基地址和表长可以改变。

TR任务寄存器

   TR寄存器保存了当前任务TSS的32位基地址,16位段选择符,段限长和描述符属性。选择符引用GDT中的TSS描述符。基地址指定TSS中字节0在线性地址空间的地址;段限长指明TSS的字节长度。

   LTR和STR指令分别用来加载和存储TR寄存器的段选择符。LTR指令加载TR寄存器的段选择符时:TSS描述符的基地址,段限长和描述符属性自动加载到TR中。在CPU刚加电或复位时,基地址默认设为0,段限长设置为0FFFFH。

    当发生任务切换时,新任务的TSS段描述符和段选择符自动加载到TR寄存器中。在写入新的TSS信息到TR中之前,TR不会自动保存旧的信息。

控制寄存器

    控制寄存器(CR0,CR1,CR2,CR3和CR4)决定处理器的操作模式和当前执行任务的特性,如图5所示。。在32位模式和兼容模式下这些寄存器都是32的。

         通过MOV CRn 指令操作这些寄存器的。控制寄存器总述如下。

CR0——包含控制系统模式和处理器状态的系统控制标志。

CR1——保留。

CR2——包含导致页错误的线性地址。

CR3——包含页目录表的物理地址和两个标志(PCD和PWT)。CR3也被称为页目录基地址寄存器(PDBR)。它只有高20位是有效的;低12位被设置为0。因此页目录必须是4KB对齐。PCD和PWT标志控制处理器内部数据缓存区的页目录的高速缓存(它们不能控制查找缓冲区TLB的页目录信息)。

CR4——包含一组标志用于开启一些架构额外的选项,并且指明系统对某些处理器的兼容性。CR4可以通过mov指令读取和加载。在保护模式下,MOV指令允许控制CR4寄存器读取和加载(特权级0)。这种限制意味着应用程序或其它系统程序(运行在特权级1,2,3上)将不能读取的加载CR4寄存器。


图五

系统指令一览表

         系统指令操作系统级功能,比如加载系统寄存器,管理高速缓存,管理中断,设置设置寄存器。这些指令中的多数只能被操作系统或特权级为0的程序执行,其它可以被任何级别的程序执行。

         如下图列出了系统指令并且指示哪些可以被应用程序使用。




保护模式内存管理

    本节描述IA-32架构保护模式下内存管理机制,包括段机制和页机制。

内存管理简介

         IA-32架构内存管理机制分两部分:分段机制和分页机制。分段机制使代码段、数据段和堆栈段相互独立,这样多任务才可以在一个处理器上运行还不会被其它程序干扰。分页机制为传统需求页、虚拟内存系统提供了实现机制。其中虚拟内存系统用于实现程序代码按需映射到物理内存中。分页也使多任务之前相关隔离。在保护模式下,分段机制是必须实现的,没有模式位用来关键分段;但是分页机制是可选的。

         这两种机制可以支持章任务系统、多任务系统或者支持共享内存的多处理器系统。如图6所示,分制机制提供处理器可寻址内存空间(线性地址)到段地址空间的地址变换。段可用来存放数据、代码、堆栈以及系统数据结构TSS和LDT。假如一个处理器上有多个任务(程序)同时运行,那么每个任务都有自己段集。然后处理器可以加强这些段之前的界线,并且确保一个程序不会通过写入另一个程序的段而干扰程序的执行。段机制对段进行了分类,这样对特别类型的段的访问能够受限制。

         一个系统中的所有段都在处理器的线性地址空间中。为了定位段中的一个字节,必须提供逻辑地址(logical address,也叫远指针)。一个逻辑地址由段选择符和偏移值组成。段选择符唯一标志一个段,除此之外,段选择符还提供描述符表(比如GDT)中称为段描述符的数据结构的偏移地址。每一个段都有一个段描述符,它用来指定段的大小,访问权限,段的特权级,段类型和线性地址空间中的段基地址。逻辑地址中的偏移地址加上段基地址就可以定位段中的一个字节。所以基地址加上偏移地址就形成了处理器线性地址空间中的线性地址(linear address)。

         假如没有启用分页,那么处理器的线性地址就直接映射到物理地址空间。物理地址空间的大小范围由处理器的数据总线位数决定。

         因为多任务系统定义的线性地址空间通常比实际物理内存大,所以需要某种虚拟化线性地址空间的方法。虚拟化线性地址空间就是通过处理器的分页机制实现的。

         分页支持的虚拟内存环境,需要由小块的物理内存(RAN或ROM)和一些硬盘存储介质来模拟大容量的线性地址空间。当使用分页时,每个段被分成很多页(典型的页大小为4KB),页存储在物理内存或者硬盘中。操作系统通过维护一个页目录和一组页表集来跟踪页。当一个程序尝试访问线性地址空间中的地址时,处理器通过页目录和页表把线性地址空间转换到物理地址空间中,然后在物理内存位置完成相应的操作请求(读或写)。

         假如被访问的页不在当前物理内存中,那么处理器将中断当前程序执行(通过产生页错误异常),然后操作系统或执行指令从硬盘读取页到物理内存中并继续执行当前中断的程序。

         当操作系统正确的完成分页时,硬盘和物理内存的交换对于程序正确的执行是透明的。

段的使用

         IA-32架构支持的段机制可以使用在多种系统设计中。从最小的平坦模型仅仅使用分段保护程序到多段模型利用段创造壮健的操作环境从而使多任务程序可靠的执行。

         以下几节将给出一些例子用于说明系统是如何利用段来提高内存管理的性能和可靠性。


图6 分段和分页

基本的平坦模型

         系统最简单的内存模型称为“基本平坦模型”,这种模型下操作系统和应用程序都可以访问连续的、不分段的地址空间。对于系统设计者和应用程序开发者来说最大限度的隐藏了分段机制。

         为了实现内存基本平坦模型,至少需要两个段描述符,一个用来指向代码段,另一个指向数据段,如图7所示。这两个段都映射到整个线性地址空间:也就是说,两个段描述符都有同样的基地址为0和同样的段限长4GBytes。通过设置4GBytes的段限长,当访问超过出了段限长分段机制也不会产生异常,甚至所访问的地址并没有物理内存。ROM(EPROM)通常过被放在物理内存最高地址处,因为处理器一开始执行FFFFFFF0H处RAM(DRAM)被放在地址空间最低处,因为在系统复位后DS数据段基地址被初始化为0。

保护模式平坦模型

         保护模式平坦模型和基本平坦模型类似,只不过段限长的范围设置成实际物理内存大小。

当访问实际不存在的物理地址空间时会生成一个普通的保护异常,如图8所示。这种模型提供了最低级别的硬件保护来访问一些程序bug。

         这种保护模型平坦模型可以变得更复杂来提供更多保护。例如,为了分页机制中提供普通用户和超级用户的代码和数据,必须定义四个段:普通用户特权级为3的代码和数据段,超级用户特权级为0的代码和数据段。通常这些段相互重叠并且从线性地址0开始。这种平坦分段模型加上一个简单的分页结构就可以在应用程序和操作系统之间起保护作用,如果为每个任务增加一个单独的页结构,那么就可以给应用程序之前提供保护了。同样的设计被用于一些流行的多任务操作系统。


图7 基本平坦模型


图8 保护模式平坦模型

多段模型

         一个多段模型,如图9所示,充分发挥了段机制的对代码、数据结构和程序提供硬件保护的能力。每个程序都有自己的段描述符表和自己的段。段可以完全属于程序私有也可以和其它程序之前共享。

         访问权限的检查不仅仅用来保护地址越界,也可以保护某一特定段不允许操作。例如代码段是只读段,硬件可以阻击向代码段进行写操作。为段建立的访问权限信息也可以用来建议保护级别。保护级别可以用来防止未认证的应用层程序访问操作系统程序。

分页和分段

         分页可以用于以上描述的图7,图8,图9分段模型。处理器的分页机制把线性地址空间分为许多页(那些段映射的地方),如图6所示。这些线性地址空间中的页然后映射到物理内存地址空间。分页机制提供一些页级别保护功能,它可以和段保护功能一起使用或者直接代替段保护功能。例如,它可以让读写保护强制使用到基础page-by-page(?)。页机制也提供两级别保护(普通用户和超级用户),这也在基础page-by-page中指定。


图9 多段模型





本节主要讲:保护模式内存管理相关的物理地址空间,逻辑和线性地址空间,段选择符,段寄存器,段描述符。

物理地址空间

         保护模式下,IA-32架构提供了一个4GBytes(2^32bytes)正常大小的物理寻址空间。处理器可以使用地址总线录址这些地址空间。这些地址空间是平坦的,范围从0到FFFFFFFFH,这些物理地址空间可以映射到读写内存、只读内存和I/O内存。以下描述内存映射机制的内容可以分为分段和分页。

逻辑和线性地址空间

         在系统保护模式中,处理器需要经过两步完成到物理地址空间的转换:逻辑地址转换和线性地址空间分页。

         即使最小程度使用分段,处理器地址空间中的每一个字节都需要通过逻辑地址访问。一个逻辑地址由16位段选择符和32位偏移地址组成,如图10所示。段选择符确定该字节位于哪个段,偏移地址确定字节相对于段基地址的段内位置。

         处理器转换逻辑地址到线性地址。线性地址是处理器线性地址空间中的32位地址。和物理地址类似,线性地址空间也是平坦的(不分段),大小2^32bytes,范围从0到FFFFFFFFH。

线性地址空间包含所有的段和系统定义的系统表。

         从逻辑地址转换到线性地址,处理器需要做如下操作:

1.        使用段选择符在GDT或LDT中定位段的段描述符,然后读取到处理器中。(这步仅当一个新的段选择符被段寄存器加载时才需要)。

2.        检查段描述符的访问权限和段的范围以确保段是可以接受的,并且确保偏移在段限长内。

3.        偏移值和段描述符表中的段基地址相加最终形成线性地址。

         假如分页不启用,处理器直接把线性地址映射到物理地址(也就是说,线性地址可以直接送到处理器的总线上)。假如启用分页,第二步地址转换将使用,这一步将把线性地址转换为物理地址。

图10 逻辑地址到线性地址的转换

段选择符

         段选择符为16位用来标识一个段,如图11所示。它不直接直向段,而是通过指向段描述符,段描述符再定义段的信息。一个段选择符包含以下元素:

         索引:位3和位15-共13位可以在GDT或LDT中选择8192个段描述符表。处理器把索引值乘以8(一个段描述符的大小为8字节)再加上GDT或LDT中的基地址就可以定位一个描述符的位置,GDT和LDT分别存放在GDTR和LDTR寄存器中。

         TI(tableindicator)flag:位2-用来指定使用的是哪个表:TI=0,表示选择GDT;TI=1表示选择LDT。

         Requested Privilege Level(RPL):位0和位1-指定选择符的特权级。特权级从0到3,0的特权限最高。

         GDT表中的第一个描述符不能被处理器使用。指向GDT第一项(也就是TI=0,索引为0时)的段选择符叫做空段选择符。当一个段寄存器(如CS,SS)加载一个空选择符时处理器不会产生异常。但是当使用加载了空选择符的段寄存器访问内存时将会产生异常。空选择符可以用来初始化不使用的段寄存器。加载带有空选择符的段寄存器CS或SS将会产生一个一般保护性异常。

         段选择符作为指针变量的一部分对应用层程序员是可见的,但是选择符的值通过由链接程序指定或者修改,而不是应用程序开发者。


图11 段选择符

段寄存器

    为了减少转换时间和代码的复杂性,处理器提供6个可以存放段选择符的寄存器,如图12所示。每个段寄存器支持一个特定的内存引用(代码,数据和堆栈)。对于实际上任何程序执行,至少需要加载可用的段选择符到CS,DS和SS寄存器中。处理器还提供三个额外数据段寄存器:ES,FS和GS,它们可以给当前执行的任务(或程序)提供额外的可用数据段。

    对于访问一个段的程序,这个段的段选择符必须加载到一个段寄存器中。所以,虽然一个系统可以定义成千的段,但只有6个段可被立即使用。其它段可以在程序执行过程中通过加载段对应的段选择符而使其生效。


图12 段寄存器

         每个段寄存器都有可见部分和隐藏部分(隐藏部分也称“描述符缓冲”或“影子寄存器”)。当一个段选择符被加载到段寄存器的可见部分时,处理器也把由段选择符指向的段描述中的基地址,段限长和访问控制信息加载到段寄存器的隐藏部分。缓冲在段寄存器中(可见部分和隐藏部分)的信息使得处理器在进行地址转换时不需要花费额外的总线周期(周期指从段描述符中读取基地址和段限长的时间)。在一个多处理器系统中,处理器访问同一个描述符表,当描述符表被修改后,软件有职责重新加载到段寄存器。

         以下两种方法提供加载段寄存器:

1,直接使用MOV,POP,LDS,LES,LSS,LGS和LFS 等指令加载。这些指令显示的引用段寄存器。

2,隐式的加载,比如使用长指针方法的CALL,JMP和RET指令,SYSENTER和SYSEXIT指令,IRET,INT n,INTO和INT3指令。这些指令执行的时候会伴随着CS寄存器内容的改变(有时其它寄存也会改变)。

    MOV指令也可用来把段寄存器的可见部分存储到通用寄存器中。

段描述符

         段描述符是GDT或LDT表中的一个数据结构项,它提供一个段的大小、位置、控制权限和状态信息。段描述符通常由编译器、链接器、加载器或操作系统创建,而不是应用程序创建。图13表示了所有类似的段描述符的通用格式。

 

图13 段描述符

段描述符中的标志表示的含义如下:

段限长(Segment Limint)

         段限长定义了段的大小。物理器会把两个段限长位域连起来形成一个20位长的段限长值。处理器依据颗粒性标志G有不同值有两种解释段限长:

l  假如G=0,段限长范围1字节到1MByte,以字节为单位增长。

l  假如G=1,段限长范围从4KBytes到4GBytes,以4KByte为单位增加。

         处理器使用段限长有两种不同的方式,依据type域中的段扩展方向标志E来决定是上扩段还是下扩段(E=0,为上扩段;E=1为下扩段,在“代码和数据段描述类型”一节会详细描述)。

         对于上扩段,逻辑地址中的偏移值范围从0到段限长,当偏移值大于段限长时会产生通用保护异常;对于下扩段,段限长的功能刚好相反,根据下扩数据标志B(在描述D/B时会有具体解释),偏移值从段限长到FFFFFFFFH(4GB,B=1)或FFFFH(64KB, B=0),小于段限长的偏移值将会产生通用保护异常。对于下扩段,减少段限长值允许在段地址空间底部分配内存,而不是顶部。因为IA-32架构使用的栈总是向下增长的,所以这种机制方便于栈的扩展。

基地址(Base address)

         确定一个段的0字节在4GByte线性地址空间中的位置。物理器会把三个基地址位域连起来形成一个32位长的段基地址值。段基地址应该16字节边界对齐。虽然16字节对齐不是必须的,但是这种对齐允许程序通过使数据段和代码段16字节边界对齐而达到最高性能

类型(type)

         指明段或门的类型,定义段的访问类型和段的扩展方向。这个域根据描述符的类型有两种解释(数据代码段描述符和系统段描述符)。这个域的值会根据代码段、数据段还是系统段还不同。

S描述符类型(descriptortype)

         指明段的类型是系统段(S=0)或数据段、代码段(S=1)。

DPL 描述符特权级(descriptorprivilege level)

         指明段的特权级。特权限从0到3,0的特权级最高。DPL用来控制段的访问。

P 段存在标志(segment-present)

         指明段是否在内存中(P=1,在内存;P=0,不在内存)。假如P=0,当一个段描述符的段选择符加载进段寄存器时处理器产生段不存在异常。内存管理软件可以使用此标志控制在某一给定时间内把哪个段加载进物理内存中,这为管理虚拟内存除分页以外的控制。如图14显示P=0时一个段描述符的格式。当这个标志被清除,操作系统可以自由使用标为“可用”(Available)的位置来存储它自己的数据,例如不存在的段实际在什么地方的信息。


图14 当段存在位P=0时的段描述符

D/B(默认操作大小/默认栈指针大小/上界限)标示(defaultoperation size/default stack pointer size and/or upper bound)

         根据段描述符是一个可执行代码段、下扩数据段、或堆栈段完成不同的功能。(在32位代码和数据段中必须设置为1;在16位数据和代码段中必须设置为0。)

l  可执行代码段。此时这个标志叫D标志,它指明段中的指令引用有效地址和操作数的默认长度。假如D=1,则默认是32位地址和32位或8位操作数;假如D=0,则默认是16位地址和16位或8 位操作数。指令前缀66H可用来选择非默值的操作数大小,指令67H可用来选择非默认值的操作地址大小。

l  堆栈段(数据段指针由SS寄存器指向)。此时这个标志叫B标志,它指明用于隐含栈操作(如pushes,pops,calls)时栈指针的大小。假如B=1,一个32位的栈指针被使用,它存储在32位ESP寄存器中;假如B=0,一个16位的栈指针被使用,它存储在16的SP寄存器中。假如堆栈段被设置成为下扩数据段,那么B标志也用来指定栈段的上界。

l  下扩段(Expand-downdata segment)。此时标志叫作B标志,它指明段的上界。假如B=1,上界为FFFFFFFFH(4Gbytes);B=0,上界为FFFFH(64KBytes)。

G颗粒性标志(granularity)

         决定段限长域值(segment limit)的单位。当G=0时,段限长的单位为字节;当G=1时,段限长的单位为4-KByt(这个标志不会影响基地址的颗粒性,基地址的单位总是字节。)。

当G=1时使用段限长检查偏移值时,不会检查偏移值的低12位有效值。例如,当G=1时,段限长为0可以表示偏移值从0到4095。

可用和保留位(Available and reserved bits)

         段描述符的第二个双字位20是给操作系统使用的;位21是64位代码段标志,在这里不作讨论,只把它设为0就行。

数据和代码段描述符类型

         当段描述符中的S置位时,描述符用于代码或数据段描述符。类型(type)的最高有效位(第二个双字的位11)用于决定是数据段描述符(复位)还是代码段描述符(置位)。

         对于数据段描述符,类型的低三个有效位(位8,9和10)解释为已访问(accessed A)、可写(write-enable W)和扩展方向(expansion-direction E),如图15所示。根据读取标志位(位9),决定数据段是只读还是可读写。


图15 数据和代码段类型

    堆栈段必须是可读写数据段,如果SS寄存器加载一个不可写的数据段选择符将会产生通用保护异常。如果栈的大小需要动态改变,栈段段可以设置为下扩段(type域中的E标志为1)。在这里,动态改变栈段的段限长会使栈空间增加在栈的底部。如果栈段的大小需要保持静态,那么栈段可以是上扩段也可以是下扩段。

         访问位指明自从操作系统复位些位以来该段是否被访问过。当段寄存器加载段的段选择符时处理器都使此位置位(假设包含此段描述符的内存类型是可写的)。此位一直保持直到需要被明确清除。此位可用来管理虚拟内存管理和调试。

         对于代码段,类型(type)域的低三位从低到高依次解释为访问位(accessed A),可读位(read enable R),和一致性位(conforming C)。代码段依据可读位(R)的不同可分为仅执行或可读/可执行。当常量或其它静态数据以及代码指令放在ROM中时一个可读/可执行段可被使用。这里,代码段中的数据可被带CS前缀的指令或者数据段寄存器中(指DS,ES,FS和GS寄存器)的代码段的段选择符加载。在保护模式下,代码段永不能写。

    代码段可以是一致性和非一致性的。允许执行在当前特权级时向更高权级的一致性代码段进行执行转移。当向不同特权级的非一致性代码段进行执行转移时,会产生一个通用保护异常,除非使用调用门(call gate)或任务门(task gate)(相关内容可看后面文章)。不访问受保护的和处理某些异常类型(如除出错,溢出)的系统功能可以放在一致性代码段。不能被更低特权级程序访问的需要被保护的程序功能应该放在非一致性代码段。

注意

不能通过call或jump转入更低优先级的代码段执行,不管目标段是一致性或非一致代码段。如果尝试这样转变将会引起一个通过保护异常。

         所有的数据段都是非一致性的,这意味着它们不能被更低特权级的程序访问。不像代码段,数据段总是能被更高特权级的程序访问而不需要使用特别的访问门。

         如果段描述符位于ROM中的GDT或LDT中,假如软件或处理器尝试往ROM中的段描述符更新(写入)时会进入无限循环。为了防止这种情况发生,把ROM中的所有段描述符的访问位都置位。并且,从操作系统代码中移除那些试图修改ROM中段描述符的代码。



   寻址能力都达64TB了,为啥我的电脑内存只有2GB呢?其实不用纠结于这事,这64TB就是所谓的虚拟地址空间,也叫逻辑地址空间,它能够寻址这么多,只是它有这个能力,并不代表你的内存就要装这么大,你内存比它小再多也不会影响你工作,反过来,要是它的寻址能力只有1MB,而你有2GB的内存,那么那1.9GB就没有实际用处了,这就太浪费资源了。而实际上这个64TB也没有什么实际意义,因为32位的地址总线能寻址的线性地址空间和物理地址空间都是2^32=4GB。这个64TB是怎么出来的,稍后揭晓。

实模式与保护模式的来历

    我们先来说一下为什么有实模式和保护模式的区别。最早期的8086 CPU只有一种工作方式,那就是实模式,而且数据总线为 16位,地址总线为20位,实模式下所有寄存器都是16位。而从80286开始就有了保护模式,从80386开始CPU数据总线和地址总线均为32位,而且寄存器都是32位。但80386以及现在的奔腾、酷睿等等CPU为了向前兼容都保留了实模式,现代操作系统在刚加电时首先运行在实模式下,然后再切换到保护模式下运行。

再来区别下几个基本概念:逻辑地址、线性地址和物理地址。这些概念一时没领会没关系。继续往下看。

三种地址

逻辑地址:即逻辑上的地址,实模式下由“段基地址+段内偏移”组成;保护模式下由“段选择符+段内偏移”组成。

线性地址:逻辑地址经分段机制后就成线性地址,它是平坦的;如果不启用分页,那么此线性地址即物理地址。

物理地址:线性地址经分页转换后就成了物理地址。

实模式寻址方式

    刚才说了8086CPU数据总线为16位,也就是一次最多能取2^1664KB数据,这个数据也解释了实模式下为什么每个段最大只有64KB。但刚才还说了其地址总线为20位,这样它能寻址的能力其实是2^20=1MB,这也就是实模式下CPU的最大寻址能力。既然它有1MB寻址能力,那怎么用16位的段寄存器表示呢?

    这就引出了分段的概念,8086CPU1MB存储空间分成许多逻辑段,每个段最大限长为64KB(但不一定就是64KB)。这样每个存储单元就可以用“段基地址+段内偏移地址”表示。段基地址由16位段寄存器值左移4位表达,段内偏移表示相对于某个段起始位置的偏移量。比如:

SEG=0x07c0

jmpi offset, #SEG

offset: mov ax,cs 

保护模式寻址方式

    在定义“逻辑地址”时看到保护模式和实模式的区别在于它是用段选择符而非段基地址,这也许就是保护模式的真谛所在,从段选择符入手,全面理解保护模式编程基本概念和寻址方式。

分段机制

    下面来看下保护模式是怎样通过“段选择符+段内偏移”寻址最终的线性地址或物理地址的。

逻辑地址到线性地址转换,这里的逻辑地址即指保护模式下的“段选择符+段内偏移地址”,如果不启用分页管理的情况下,那么此线性地址即最终的物理地址。

逻辑地址到线性地址转换

    在理解此图时必须要明白段选择符结构、描述符表概念等,下面就一一介绍。

如图2段选择符结构,段选择符为16位,它不直接指向段,而是通过指向的段描述符,段描述符(一会介绍)再定义段的信息。

段选择符结构

    其中TI用来指明全局描述符表GDT还是局部描述符表LDTRPL表示请求特权级,索引值为13位,所以从这里看出,在保护模式下最多可以表示2^13=8192个段描述符,而TI又分GDTLDT(如图3所示),所以一共可以表示8192*2=16384个段描述符,每个段描述符可以指定一个具体的段信息,所以一共可以表示16384个段。而图1看出,段内偏移地址为32位值,所以一个段最大可达4GB,这样16384*4GB64TB,这就是所谓的64TB最大寻址能力,也即逻辑地址/虚拟地址。

    在保护模式实际编程中,如下一条语句:jmpi 0, 8。其中的8即段选择符,8的二进制表示为:0000 0000 0000 1000b,所以这条语句的意思是跳转到GDT表(TI0)中的第2个(段描述符表从0开始编号,所以这里的1指表中的第2个)段描述符定义的段中,其段内偏移为0

3

    下面再来看段描述符结构,段描述符表中的每一项为一个段描述符,每一项为8字节,其结构如图4所示。

    从图中可知,段选择符指向的段描述符里有三个部分基地址信息,这三部分组成一个32位地址就决定了段基地址位置,此地址再加上段内偏移最终确定线性地址位置。

    段描述符中的S位和TYPE字段(四位)的不同又分为数据段描述符、代码段描述符(S=1)和系统段描述符(S=1)。数据段和代码段描述符类型如图5所示。

图5

    系统段描述符如图6所示。

6

分页机制

    到目前为止我们知道了保护模式下是怎样通过段选择符指向一个段描述符,最终由段描述符+段内偏移定位线性地址,在不启用分页情况下,此线性地址就是物理地址,那么在启用分页情况下,又是怎样实现内存的映射转换的呢?这就是伟大的分页机制。

    分页机制如下图所示,它把物理内存分成相同固定大小的页面,2^12=4KB。每个页面的0~4KB范围由线性地址的低12位表示,线性地址空间的高10位用来指定页目录中的位置,可以选择2^10=1024个目录项,每个目录项为四字节,所以页目录为1024*4B=4KB。每个目录项中的高20位用以查找页表在物理内存中的页面,每个页表含1024个页表项,每个页表项也是四字节,这样一页表也是1024*4B=4KB。所以一个页目录可以查找1024个页表,每个页表为4KB,所以总共可以查找的页表大小为1024*4KB4MB大。最后每个页表项的高20位用以定位物理地址空间中的某个页基地址,此地址再加上线性地址空间的偏移值就是最后物理内存空间单元。目录项和页表项结构如图8所示。

7分页机制

8

    从一个逻辑地址经过分段和分页寻址物理地址的整个过程就如图9所示。总的来说整个过就是逻辑地址经分段机制变成线性地址,如果不启用分页的情况下,此线性地址就是物理地址;如果启用分页,那么线性地址经分页机制变成物理地址。

9分段和分页

分段和分页的意义

    说了半天分段和分页的原理,它们到底有何用?这里以保护模式下分段和分页讲解。

10 多段模型

    如图10所示。一个多段模型充分发挥了段机制的对代码、数据结构和程序提供硬件保护的能力。每个程序都有自己的段描述符表和自己的段。段可以完全属于程序私有也可以和其它程序之间共享。

访问权限的检查不仅仅用来保护地址越界,也可以保护某一特定段不允许操作。例如代码段是只读段,硬件可以阻击向代码段进行写操作。

分页为需求页、虚拟内存提供实现机制。具体的实现机制可以再深入学习。



你可能感兴趣的:(笔试面试,learning)