zhangyachen / zhangyachen.github.io

zhangyachen's blog
274 stars 39 forks source link

cpu 如何实现保护模式 #129

Closed zhangyachen closed 6 years ago

zhangyachen commented 6 years ago

8086处理器只支持实模式。在实模式下,下面的指令都会向特定的内存中写入一个字节的数据,不管这块内存存放的操作系统的数据还是其他任务的数据(除了ROM区域外)。

mov byte ptr [bx],'a'

这肯定是不能被人接受的,所以在1982年Intel公司推出的80826处理器中提出了保护模式的概念,我们在访问内存时,处理器会检查段的基地址、段的界限以及特权级别等。可是由于80286的寄存器还是16位,所以偏移地址仍然不能超过64KB,这就限制了80286处理器的应用,很少为人所知。 1985年Intel公司推出的80386处理器是Intel公司的第一款32位产品,获得了极大的成功,是后续所有32位产品的基础。和80826以及8086比,寄存器和地址线都是32位。

那么处理器是如何通过保护模式来限制程序对内存的访问的?

作为对比,我们先来看一下实模式下(8086)如何访问内存的: 8086的寄存器是16位的,理论上只能访问2^16大的内存(64KB),但是这确实有点太小了。于是8086将地址线扩充到了20根,即可以访问2^20大的内存(1M)。但是由于寄存器暂存的地址是16位的,所以8086将寄存器内的地址左移4位(乘以16),即可产生20位的地址。可以看出,这样产生的地址是16的倍数,不能访问任意位置的内存,所以我们再加一个寄存器来存放偏移地址,达到能够访问任意内存的目的(这个过程也叫给内存分段,但是内存本身是没有分段的)。即在8086中,*物理地址=段地址 16 + 偏移地址:**

mov ax,100h
mov ds,ax                 ;将段地址输送到ds段寄存器
mov bx,100h               ;将偏移地址输送到bx寄存器
mov byte ptr ds:[bx],‘a’    ;实际访问的物理内存地址是100h * 16 + 100h

(图)

文章开头提过,实模式下访问内存不受保护。那么保护模式下是如何限制程序对内存的保护的呢? 其实容易想出,为了达到不能任意访问内存的目的,我们需要知道使用一个段时的界限,这样就可以计算出引用一个段时允许访问的物理内存值(即允许访问的物理内存值 <= 段的基地址 + 段的界限值)。 上面说的段界限在保护模式下存放在段描述符中(Segment Descriptor),大小为8字节。上面说过的每一个段都需要一个段描述符。所有的段描述符都集中存放在一起,构成一个描述表。 最主要的描述表是全局描述表,叫作GDT(Global Descripotr Table)。进入保护模式前,必须先定义全局描述表。由于进入保护模式前是实模式的,而实模式(8086)的内存为1MB,那么GDT一般都定义在1MB以下的内存中。刚才为什么说最主要的?因为还有LDT(Local Descriptor Table),稍候会说到。

下面是段描述符的组成:

(图)

可以看出,段地址是32位,段边界是20位。实模式下,之前已经提过怎么通过段地址和偏移地址形成物理地址的。在保护模式下,段地址即物理地址(未开启分页,如果开启分页,需要经过页映射的转化才能形成物理地址)。段界限用来限制段的扩展范围。 G代表粒度。0代表段界限以字节为单位,1代表段界限以4KB为单位。 其他位在之后用到会说。感兴趣的可以看下参考资料。

在32位处理器上,寄存器的结构也发生了变化