CPU保护模式深入探秘

原文链接为:http://www.chinaunix.net/old_jh/23/483510.html

保护方式的体系结构

主要问题:
          保护方式的寄存器模型
          保护方式的描述符与页表项
          保护方式的存储器管理与地址转换
          多任务机制与保护实现
          虚拟 8086 模式

一、保护方式的寄存器模型
   新增四个寄存器 (指针 -----  指向内存中的特殊的数据表):
  全局描述符表寄存器 GDTR
  局部描述符表寄存器 LDTR
  中断描述符表寄存器 IDTR
  任务寄存器 TR

 1、GDT 与 GDTR
 全局描述符表在系统的物理存储器地址空间中定义一个称为全局描 述符表GDT的数据表。
 而GDTR则是在CPU内的一个48位的寄存器:
  16位限长:15位 --- 00位    (比实际值小1)
  24位基址:39位 --- 16位    (共32位地址空间)
   8位基址: 47位 --- 40位
 全局寄存器是一种可能被许多任务共享或独立使用的通用的系统存储资源,有GDT进行管理。该表包含一项分段描述符,用以表识全局存储器中的段。每个描述符8字节,这样64KB的段中可以容纳8192个描述符。
 主要的描述符有:全局描述符GDTR、局部描述符LDTR、中断描述符IDTR

 2、LDTR
 局部描述符LDTR也是存储器管理支持机制的有机部分。每个具体的任务都要访问全局描述符表及其管辖的存储器,也要访问自己的局部描述符表及其管辖的存储器。这种局部专用的描述表称为“局部描述表LDT”,它定义了任务用到的局部存储器地址空间,其中的段描述符用以访问当前任务存储器段中的数据和代码。
 由于每个任务都有自己的存储段,保护模式的体系中就可能会包含许多局部描述符表。
 LDTR 并不直接定义一个局部描述表,仅是指向GDT中的LDT描述符的选择符Selector。如果LDTR中装入了Selector ,相应的描述符就能够从全局存储器中读出并装入局部描述符表高速缓存中。只是描述符确定了局部描述符表。将描述符装入高速缓存就为当前任务创建了一个LDT,即每次LDTR中装入Selector,则局部描述符表的描述符被缓存,激活了一个新的LDT。

 3、IDTR
 中断描述符表IDT中的描述符不是段描述符,而是一种“门”描述符,它提供一种机制,它将程序控制给中断并执行中断服务程序。
    限长:位15 --- 位00
    基址:位39 --- 位16
    位47 --- 位40 
 中断描述符表也是8个字节长,故64KB的一个段中可容纳8192个“中断描述符”。

 4、CR
 保护模式模型包括四个系统控制寄存器:CR0 ~ CR3
 CR0:  PE(保护模式允许)、MP(数学协处理器存在)、
                  EM(协处理器模拟)、TS(任务切换)
    R(协处理器扩展类型)、PG(分页)
      其中PE、MP、EM、TS、R 也就是常说的MSW(机器状态字)
 CR1: 保留
 CR2: 缺页线性地址
 CR3: 页目录基址寄存器(PDBR)

 5、TR
 任务寄存器TR提供了高档微处理器的保护模式任务切换机制。TR存放一个16位索引值的选择符。TR开始的选择符由软件装入,它开始第一个任务,之后在执行任务切换指令时就自动修改选择符。
 TR中的选择符用来指示全局描述符表中描述符的位置。当选择符装入TR时,相应的任务状态段TSS描述符自动从存储器中读出并装入任务描述符缓存(48位)中。该描述符定义了任务状态段TSS的存储块,提供段起始地址BASE和段大小LIMIT。
 每个任务都有自己的TSS。TSS包含启动任务所必需的信息。


 
 6、其它功能寄存器
 一些在实模式和保护模式下都可以使用的寄存器在模式切换时,其功能会发生变化。
 如实模式的段寄存器( CS、SS、DS、ES),其值是段的基地址。
 CS    =   Code Segment
 如保护模式的段选择符寄存器( CS、SS、DS、ES),其值不再是段的基地址,而是段的选择符。CS   =   Code Segment Selector 
 
 AX、BX、CX、DX也被扩展为32位的 EAX、EBX、ECX、EDX
与其相关的指令也增加了,如 MOV类、ADD类、....
 IP、SP、BP、SI、DI 也被扩展为32位的 EIP、ESP、EBP、ESI、EDI

二、 保护方式的描述符与页表项
 高档微处理器的保护方式支持多种“描述符”,他们分别服务于不同的系统功能,如系统段描述符、局部描述符、调用门描述符、任务状态段描述符、任务门描述符、中断描述符,等等。
 “描述符”是用以管理64T字节虚拟存储空间分段的基本元素。一个描述符对应于虚拟存储空间中的一个存储“段”,它负责将代码、数据、堆栈和任务状态段的“虚拟地址”映像到线性地址,并给段指定“访问属性”。
 每个描述符8字节,
 BYTE1~0                 LIMIT :  位 15 --- 位0      (或OFFSET)
 BYTE6          的低四位         位 19 --- 位16           
 BYTE4~2                BASE:  位 23 --- 位0 
 BYTE7                    BASE:  位 31 --- 位24

 BYTE5         位0   A  1访问过
       类型  W可写、R可读、C证实、E可执行、ED扩充方向
      S  1段描述符 或 0门描述符
        DPL     描述符特权级 (2位)
        P          出现位: 1段影射到物理存储器,0不存在影射 
 BYTE6          的高四位
        AVL 可为编程者使用
        G  粒度 
        X
 


三、保护方式的存储器管理与地址转换
      保护方式的存储器组织相对而言比较复杂,MMU主要管理地址空间以及虚拟(逻辑)地址转换成物理地址,包括“分段”和分页模型。


 1、虚拟(逻辑)地址与空间分段
  MMU使用48位存储器指针(选择符16::偏移量32),称为虚拟地址,用以指定数据/指令的存储器位置,其中Selector存放于段选择符寄存器(CS、DS、ES、SS)中,用以“段”选择。而偏移量可以存放在其他可访问的寄存器中。
 32位的偏移量指针规定了4G的段容量: 1 BYTE ~ 4GB。
 16位的Selector 又分为 13 位索引、1位表选择位、2位请求特权级
 这样14位的“段选”和32位偏移量,就得到一个46位的“地址”,寻址64T ( 40位 = 1T)。

 在分段模型中: 64T 的空间 = 低32T全局空间 + 高32T的局部空间
    Selector中的“表选”位指示“全局”或“局部”
    每个空间中允许最多8192个存储段。
    段描述符定义段属性,并不是所有的段都在使用
 当启动一个任务时,可以激活全局存储器段和局部存储器段。
    局部段中存放每个任务的“局部内容”,所有任务共享“全局内容”。

 2、物理地址和虚拟地址转换
 对程序员“透明”的空间为64T的逻辑空间。但CPU的32位地址总线仅支持 4G 的连续物理空间。由此一次只有“虚存”部分信息于物理存储段中,而临时“不用的”段可以都存放在“辅存”上。
 如果访问一个不在“物理存储器”上的段,且还有物理空间,则读入;如果已没有物理空间了,则必须实现“交换”,即将别的不用的段送到“辅存”上给新信息腾出空间。OS存储器管理控制内存的分配、释放、交换。

 分段和分页机制均支持48位虚拟地址 ==》32位物理地址的映射。
        映射过程: 
  (1) 段转换:   对虚拟地址进行段转换
  (2) 地址转换:   如果禁止分页,则产生的线性地址就是物理地址;
     如果允许分页,则产生的线性地址
           再经过分页转换才产生物理地址
 同时,MMU 还确定虚拟地址空间的相应“段”或“页”是否在“物理存储”中。如果存在则操作如常,否则“交换”装入该段或页。

 2.1 段式地址转换
  段选择符寄存器中的段选择符Selector的“表选位”选择进入“全局”或“局部”的段描述符缓存寄存器中。如图:
    (注意:定义段属性的是描述符而非选择符)。

 每个段选择符寄存器Selector 都有一个64位的内部段描述符缓存寄存器,在指令执行时“透明”地装入描述符。

 例如  MOV DS,AX
 执行时AX中的选择符装入DS,然后从存储器中读出相应局部描述符表中的描述符,并装入DS对应的段描述符缓存寄存器中(如果描述符已经被缓存则直接引用即可)。MMU然后检查描述符中信息的有效性。
 而段中的“内容”则由虚拟地址中的32位偏移量来确定。

 实际上,给段描述符缓存寄存器装入值,就实现从16位的选择符Selector 到对应的32位段基址的映射。段描述符缓存寄存器中的“描述符”将随任务的执行而动态改变。MMU只允许6个存储器处于活动状态, 分别对应CS、DS、ES、SS、FS、GS

 2.2 页式地址转换
 如果禁止分页,则每段最多可以分配1BYTE ~4GB的物理空间;如果允许分页,则物理空间被划分成 1048496 页,每页4096BYTE。
 分页存储机制在分段存储管理机制下工作,如果允许分页则地址空间组织有所不同。

 分页机制简化了MMU程序的实现。段转换过程产生的线性地址不再用作物理地址,还要经过第二个转换过程(页转换)。
 线性地址 = 10位目录域、10位页域、12位偏移地址域

 存储器页目录表的地址由CR3中的页目录基址寄存器PDBR确定。PDBR的 这20位实际上是基址的MSB,低12位指示页内地址。这样页目录就包含4K字节存储器地址,所有的页目录组成1K个32位的地址。这些地址每个都指向一个物理存储器中的页表。
 
 10位目录域是相对于PDBR的偏移量,其中PDBR在页目录表中选中一个1K的32位页目录项。该指针被缓存在转换检测缓存器中,用作存储器中页表的基址。如同页目录,每个页表也是4K,包含1K个32位地址,称为页帧地址。每个页帧地址指向物理存储器中的4K数据帧。

 线性地址的10位页域从页表中选择一个1K的32位页表项(也被缓存在转换检测缓存器中)。

 转换检测缓存器可以保存32个表项,因此共有128K字节的存储区总可以直接被访问,无须从页表中读出而可直接存取。如果要访问的“内容”不在这些页中,则存在将页表项读进转换检测缓存器的额外开销。

四、 多任务机制与保护实现

 80286以上的高档微处理器实现了多任务的软件体系结构。所谓任务是指其硬件允许软件系统中存在多个任务并能够以分时的方式安排进行,即程序控制在一段固定长度时间后从一个任务转到另一个任务。

 比如,任务可以循环方式执行,即最近执行的任务回到执行清单的末尾。尽管该过程时以分时的形式进行,CPU执行起来还是会让"人"觉得所有任务都是同时运行。

 所谓任务指执行特定功能的程序集合,也可称作过程。软件系统通常需要执行许多过程。在保护模式的微处理器系统中,每个过程都是独立的任务,CPU提供一种称为任务切换的高效机制保证在任务之间进行切换。例如,一个以16MHZ运行的80386DX执行任务切换操作只需要19us。

当一个任务调入运行时,它既有全局又有局部存储器资源。局部存储器地址空间按任务进行划分,这意味着每个任务一般都有自己的局部存储器段。全局存储器中的段可被所有任务共享。从而,一个任务可以访问全局存储器中的所有段。

1、 保护和保护模式

 可以在保护模式软件系统中加入防护措施以拒绝任务存储器资源未经授权或不正确访问。这种存储器防护措施称为保护,CPU硬件实现了一种保护机制。在多任务环境中,该机制对任务的局部和系统资源访问作了限制,使得各个任务之间隔离开来。

 分段、分页和描述符是CPU 保护机制的关键元素。在分段存储器模型中,段是具有一项保护属性的虚拟存储器地址空间中的最小单位。其属性由段描述符中的访问权限信息和限长域定义。在存储器访问过程中硬件保护机制执行一系列检查。举个例子,当写存锗器缓存一致,还要检查偏移量以确定该区在段限长之内。CPU加在软件上的保护性检查和限制清单:
 类型检查、限长检查、地址域限制、过程入口点限制、指令集限制

 回顾一下段描述符中访问权限信息的有关属性。P位定义该段是否在物理存储器中。假定该段在物理存储器,类型域的第4位C/D说明它是代码段还是数据段(0表示数据段,1表示代码段)。其他诸如可读ER、可写EW、证实EC、向上ED和向下扩充以及是否已访何过A等段属性由类型域中其他位指定。特权级由DPL域指定。

当一段被访问过后,其基址和限长要缓存到CPU中。但是在装入描述符之前MMU要验证选中段当前是否在物理存储器中,当前程序是否由可访问的特权级,其类型是否同目标段选择符寄存器(CS代码段,DS,ES,FS,GS或SS堆栈段)一致,以及对该段的引用没有超出段边界。如果一项违反则发出一错误报告。存储管理程序可以确定原因,解决问题并重启操作。

 违例一:  如果装入CS的选择符指向数据段的描述符,则类型检查产生         违规错误。
 违例二:  试图读标为不可读的代码段中操作数。
 违例三: 如果试图访问的数据字节偏移量大于LIMIT,或双字的偏移量
        大于等于LIMIT-2,那么都会超出数据段边界,导致违反保护性
        措施。

 CPU为每个任务提供四种特权级: 0、1、2和3。其中 0级是最高的特权级,3级是最低的特权级。

系统软件和应用软件通常划分: 内核、系统服务、常规扩展、应用程序......

核心代表提供面向微处理器功能(I/O控制、任务安排和存储器管理)的应用程序独立的软件。出于此原因它是最高的特权级0级。
级1  是提供例如文件访问的系统服务的过程。
级2  用于实现一些支持系统专用操作的例程。
级3  是用户的应用程序运行的级别。

这种划分还显示了是如何利用特权级将系统级软件(从级0到级2)同用户级应用程序(级3)分开的。某一级别的任务可以使用更高级的程序但是不能修改其内容。这样应用程序就可以使用高3级的系统程序而不必担心破坏其完整性。

 最后,指令集上加有一些保护性措施。如系统控制指令只能在保护级为0的代码段中执行。

 每个任务都有它自己的局部描述符表。因此,只要该任务的局部描述符表中的描述符不能被其他任务引用,那么就将该任务同其他任务隔离了开来。也就是说,该任务已经指定了一块独立的虚拟地址空间。

 以上说明了段、特权级和局部描述符表为任务中的代码和数据提供了保护性措施。这类保护措施提高了软件的可靠性,因为一个应用程序中的错误不会影响到操作系统和其它应用程序。

再看一看特权级是怎样指定给代码段或数据段的。

任务运行的时候既可对局部又可对全局的代码段、数据段和堆栈段访问。特权级通过段描述符中访问权限信息指定给每个段。只要将级别号写入DPL位就可对段指定任意特权级。为提供更多的灵活性,输入/输出有两个特权级。

首先I/O驱动程序属于系统资源,指定一个特权级。I/O控制程序属于核心的一部分,它指定了特权级0。

指令 IN、INS、OUT、OUTS、CLI和STI称为信任指令。因为CPU保护模型给它们的使用加上了额外的限制。它们只能在大于或等于IOPL (输入/输出特权级) 码的特权级上执行。

 IOPL是I/O的第二个特权级。IOPL位是在保护模式的标志寄存器中。IOPL位必须通过软件指定要给输入/输出指令的特权级。IOPL值随任务不同而不同。

 假定高于特权级3的I/O指令限制应用程序直接执行I/O,那么应用程序如果要想进行I/O操作,它必须通过操作系统的I/O驱动程序请求程序。

2、在保护模式下访问代码和数据

在任务执行过程中,CPU可能需要将控制转给其他优先级的程序,或者访问不同特权级的段中数据。访问不同特权级的段中代码或数据通常有严格的限制,这些规则将保证高特权级的代码或数据不被低特权级的程序所破坏。

 在讨论程序怎佯访问相同或不同特仅级中的数据之前,让我们先知道一些关于特权级的术语。我们曾经使用过术语DPL(描述符特权级)和IOPL(I/O特权级)。在讨论数据或代码访问中的保护机制时还用到了两个术语 CPL(当前特权级)和RPL(申请特权级)。CPL是任务当前访问的代码或数据段的特权级。
 例如一个正在运行的任务的CPL是描述符缓存中访问权限字节的DPL。该值一般就是代码段的DPL,RPL是新装入段寄存器的选择符的特权级。例如对于代码而言,RPL就是包含被调用程序的代码段的特权级,也就是说,RPL是程序控制要转交给的代码段的DPL。
 任务运行时可能还需要访问处于其他特权级的段中程序。从而随着程序的执行,任务的当前特权级也将动态改变,CPL通常切换到当前访问的代码段的DPL。

 CPU保护规则决定程序能访问哪些代码或数据。在讨论程序控制怎样转给不同保护级的代码前,我们先看一看数据段怎样被当前特权级的代码所访问。
数据访问所作的保护级检查(图832):

一般规则是代码只能访问相同或较低特权级的数据。例如,任务的当前特权级为1,那么它能访问DPL为1、2或3的数据段中操作数。只要DS、ES、FS或GS寄存器装入了新的选择符,那么就得检查目标数据段的DPL以确保它小于等于CPL和RPL得最大特权级。如果DPL满足条件,则描述符被缓存入CPU中,可以访问数据。

 这条规则的一个例外是SS寄存器赋值时,DPL必须等于CPL。也就是说,活动的堆栈(每个特权级有一个)总是处在CPL级。

【例12】  假定 DPL=2,CPL=0,RPL=2,可以进行数据访问吗?
【解】:   目标段得DPL是2,该特权级小于CPL=0,0级是CPL和RPL中得最大特权级。
  因此满足保护标准,可以进行数据访问。


 程序控制(JMP, RET)在相同特权级和不同特权级的代码之间转移时所运用的规则也是不同的,为将程序控制转给同一代码段中的另一条指令,只须使用转移或调用指令即可。这两种情况中,只需要检查限长以确保转移或调用得目标不会超过代码段得边界。

为将控制转给相同或不同特权级的另一段中代码,心须使用远转移或调用指令。对于这类程序控制转移,既要检查限长又要检查类型。发生程序控制转移需要两个条件。
首先如果CPL等于DPL,则两段处于相同得保护级转移发生;
其次如果CPL代表的特权级比DPL高且新段类型域中的证实码(C位)置1,那么程序在CPL级执行。

将控制转移给不同特权级的段中代码时所遵循的一般规则是新代码段的特权级必须更高。一种称为门描述符的描述符用来实现特权级的改变。将控制转给高特权级代码段的指令依旧是远调用或远转移指令。这次指令并不直接规定目标代码的位置,它是引用一个门描述符。这种情况下CPU执行更为复杂的程序控制转移机制。

 4种类型的门描述符:调用门、任务门、中断门和陷阱门。调用门实现任务从CPL级到更高特权级的间接控制转移。它在更高特权级段中定义了一个有效入口点,调用门的值就是入口点的虚拟地址:目标选择符和目标偏移量。目标偏侈量指向该段中要执行的指令,调用门可以放在GDT或LDT中。

调用指令包括偏移量和选择符。当指令执行时,该选择符装入CS并指向调用门。反过来,调用门又会导致它的目标选择符装入CS。这导致被调用代码段的描述符被缓存,该代码段描述符位存储器代码段提供了基址。注意到调用门描述符中的偏移量将定位代码段中的入口点。

 每当任务的当前特权级改变时都会激活一个新的堆栈。作为程序上下文切换序列的一部分,旧的ESP和SS随同旧的EIP和CS以及其它参数被保存到新的堆栈中,需要保存这些信息用以返回旧的程序环境。现在高特权级的过程就开始执行。

 执行结束时RET指令将程序控制返回给调用的程序。RET指令将导致旧EIP和CS值,一些参数及其旧的ESP和SS值从堆栈中弹出。这就恢复了原来的的程序环境。现在程序则从低特权级代码段中调用指令的后一条指令开始执行。对于成功的调用门的DPL必须等于CPL,被调用码的RPL必须比CPL特权级更高。

3、任务切换和任务状态段表

 任务是CPU多任务软件体系结构中的关键元素,并指出它的另外一个重要特点就是高性能的任务切换机制。任务可以间接或直接地被激活,这只需要执行段间转移或段间调用指令即可。在利用转移指令启动任务切换时,不保存前一个任务的返回链接。但是如果利用调用切换到新任务,那么将自动保存返回链接信息,该信息保证新任务执行完后返回到旧任务中调用指令后面的一条指令。

CPU执行的每一个任务都指定了一个选择符,称为任务状态选择符。该选择符是全局描述符表中任务状态段描述符对应的索引。

 如果转移或调用指令将任务状态选择符作为其操作数,那么任务有一个直接的入口。执行一条调用指令时,选择符装入CPU的任务寄存器(TR)。然后从GDT中读出相应的任务状态段描述符并装入任务寄存器缓存,以上只有在描述符的访问权限信息声明的条件满足时才会发生。也就是说,描述符在物理存储器中(P=1);任务不是忙状态(B=0);未违反保护性措施(CPL必须等于DPL);装入之后描述符中的基址和限长将定义任务状态段(TSS)的初始点和大小。该TSS包含启动或停止任务的所有信息。

任务段中的内容。
典型的TSS最小为103字节。这样,TSS描述符中能够声明的最小限长为67H。注意到段中包含以下信息:用以启动任务的微处理器状态(通用寄存器、段选择符、指令指针和标志),该任务被调用时前一个活动任务TSS的返回链接选择符,局部描述符表寄存器选择符,特权级微0、1和2的堆栈选择符和指针,以及I/O允许位。

任务启动的过程。
假定新任务被调用前已有一活动任务,则新任务就是所说的嵌套任务,它导致标志字的NT位置为1。在这种情况下当前任务首先被挂起,然后CPU的用户可访问寄存器状态被保存到旧的TSS中。接着,新任务描述符中的B位被标为忙。机器状态字中的TS被置1以表明任务处于活动状态;新任务TSS的状态信息被装入MPU;旧TSS的选择符作为返回链接选择符被保存到新的任务状态段中。任务切换操作至此结束,然后从由代码段选择符(CS)和指令指针(EIP)新值确定的指令处开始执行。

旧的程序上下文通过将旧的TSS选择符作为返回链接选择符保存到TSS中而得以保留。在新任务得末尾执行返回指令,那么返回链接选择符将自动重新装入TR。这就激活了旧的TSS,前面的程序环境得以恢复,现在程序从旧任务中离开的地点开始执行。

 间接激活任务的方法时转移到或调用一个任务门。这是一种将控制传给其RPL比CPL高的任务的方法。这次指令包括指向任务门的选择符,而不是任务状态选择符,该选择符可能在GDT或LDT中。门中的TSS选择符装入TR用以选择TSS并启动任务。

 一个任务切换原则的例子。
 图841中的表包含TSS描述符SELECT0到SELECT3。这四个描述符分别包含任务0到3的访问权限和选择符。为激活存放在数据段中选择符SELECT2所代表的任务,可以使用如下过程。首先,将数据段寄存器装入地址SELECTOR_DATA_SEG_START,它指向包含选择符的段。可用如下指令:
MOV   AX, SELECTOR_DATA_SEG_START
MOV   DS, AX
 由于每个选择符8字节长,SELECT2就距段头16字节处,将该偏移量装入寄存器 EBX:
 MOV   EBX, 0F
 此时可以用如下指令实现段间转移:
 JMP  DWORD PTR [EBX]
该指令执行将把程序控制交给由描述符SELECT2中选择符规定的任务。在这种情况下不保留程序链接。如果用调用指令:
 CALL  DWORD PTR [EBX]
则保留链接。

五、虚拟 8086 模式

8086/8088应用程序,例如那些为PC系列机操作系统编写的程序可直接运行在高档CPU的实模式下。保护模式的操作系统例如UNIX也可以不加修改地运行DOS应用程序。这是由虚拟8086模式完成的。CPU支持在此模式下的8086CPU编程模型,并且可直接运行8086/8088中的程序。或者说它创建了一个为执行程序用的虚拟8086机。

对干此类应用,CPU将在保护模式和虚拟8086模式之间来回切换。 UNIX操作系统和UNIX应用程序运行在保护模式,如果要运行DOS操作系统和DOS应用程序,那么CPU将切换到虚拟8086模式。该模式切换由称作虚拟8086管程的程序控制。

虚拟8086模式由扩展标志寄存器中的虚拟模式(VM)位选择。VM切换到1则允许虚拟8086模式操作。实际上EFLAGS中的VM位并不是由软件直接切换到1。这是因为虚拟8086模式一般是以保护模式任务进入的。从而,拷贝EFLAGS的值应该包括VM等于1,给EFLAGS赋值也是任务切换过程的一部分。这样反过来则启动了虚拟8086模式。虚拟8086程序运行在特权级3。虚拟8086管程负责设置和清除任务对EFLAGS的拷贝中的VM位,并且允许保护模式任务和虚拟8086模式任务同时存在于多任务程序环境中。

另外一种启动虚拟8086模式的方法是通过中断返回。在这种情况下EFLAAGS从堆栈中重新装入。同样,EFLAGS拷贝中的VM位应先设为1以进入虚拟8086模式操作。

http://blog.csdn.net/sqlserverdiscovery/article/details/8110083

原文地址:https://www.cnblogs.com/findumars/p/5551229.html