保护模式学习笔记之基础知识
寻址方式
CPU的操作模式
1.实地址模式
简称实模式,即模拟8086处理器的工作模式。此模式下的IA-32处理器相当于高速的8086处理器。实模式提供一种简单的单任务环境,可以直接访问物理内存和I/O空间,由于操作系统和应用软件运行在同一个内存空间中和同一优先级上(就是他们的权力是一样的),应此操作系统的数据很容易被应用程序破环。DOS操作系统就是工作在实模式下。CPU复位(resnet)或加电(power-on)的时候以实模式启动,处理器以实模式工作。
实模式最开始出现于早期的8086CPU的时期,当时的CPU只有20根的地址线,此时的地址空间就是1M。用来寻址的是8个16位的通用寄存器,4个16位的段寄存器,但是16位的寄存器只能寻址64K的地址空间,为了让16位的寄存器构成20位的主存地址,就需要采用(段基址:段偏移的方式)来寻址。
段基址由段寄存器提供,分别是cs,ds,ss,es,fs,gs。段偏移则通用寄存器来提供,为了通过这两部分构成20位的主存地址,就需要将段基址左移4位以后在与段偏移相加,这样就得到了一个20位的主存地址,也就是说此时
物理地址=段基址<<4+段内偏移。
比如此时ds段寄存器的值是0x4000,而通用寄存器di的值为0x1900,那么它们构成的地址就是ds:di=0x4000 <<4+0x1900 = 0x41900
CPU工作在这种模式下,它的寻址速度是非常快的,因为可以直接获得物理地址对其进行读写。但是它的寻址范围非常有限,且操作系统和普通的应用程序可以同样对这片空间进行读写,这样就给安全性带来了很大的问题。
##2.保护模式
所有IA-32处理器的本位模式,具有强大的虚拟内存支持和完善的任务保护机制,为了现代操作系统提供了良好的多任务运行环境。其名称中的"保护"就是保护多任务环境中各个任务的安全。多任务环境的一个基本问题就是当多个任务同时进行时,如何保证一个任务不会受到其他任务的破坏,同时也不会破坏其他任务,也就是要实现多个任务在同一系统中"和平共处,互不侵犯"。所谓任务,从CPU层来看就是CPU可以独立调度和执行的程序单位。在Windows操作系统来看,一个任务就是一个线程或进程。
进一步来说,可以把保护模式对任务的保护机制划分为**任务内保护和任务间保护**。任务内的保护是指同一任务空间内不同级别的代码不会互相破坏。任务间的保护就是指一个任务不会破坏另一个任务。简单来说,任务间的保护是靠内存映射机制(段映射和页映射)实现的,任务内的保护是靠特权级检检实现的。
随着CPU的发展,CPU有了32根地址线,此时可以表示的地址范围就是4GB。同时,通用寄存器也从16位扩展到32位的,此时只需要一个通用寄存器就可以寻址4GB的地址空间。但是,为了兼容早期的CPU,此时的寻址方式依然采用的是(段基址:段偏移)的寻址方式。
表示段基址的段寄存器由16位变成了96位,只不过可见的只有16位,且此时的段寄存器保存的就不再是段的基址,而是索引和该段的特权级,通过索引来去全局描述符(GDT)中找到对应段的表项,这里的每一个表项都是段描述符,存放了段基址,段界限等内容。通过特权级就可以实现同一个任务中不同内存具有不同的权限,这就实现了任务内保护。
段偏移由32位的通用寄存器来决定,表示的还是偏移地址,将它与段基址相加得到了虚拟地址,这个虚拟地址在经过页机制的转换才可以获得真正的物理地址,这样就可以保证一个任务无法访问另一个任务的内存。
###任务间保护
任务间保护主要是靠虚拟内存映射机制来实现的,即在保护模式下,每个任务都被置于一个虚拟内存空间中,操作系统决定何时以及如何把这些虚拟内存映射到物理内存。举例来说,在Win32下,每个任务都被赋予4GB的虚拟内存空间,可以用地址0~0xFFFFFFFF来访问这个空间中的任意地址。尽管不同任务可以访问相同的地址(比如0x00401010),但因为这个地址仅仅是本任务空间中的虚拟地址,不同任务处于不同的虚拟空间中,不同任务的虚拟地址可以被映射到不同的物理地址,这样就可以很容易防止一个任务内的代码直接访问另一个任务的数据。IA-32CPU提供了两种机制来实现内存映射:段机制和页机制。
##任务间保护
任务间保护主要是靠虚拟内存映射机制来实现的,即在保护模式下,每个任务都被置于一个虚拟内存空间中,操作系统决定和时以及如何把这些虚拟内存映射到物理内存。举例来书,在Win32,每个任务都被赋予4GB的虚拟内存空间,可以用地址0~0xFFFFFFFF访问这个空间中的任意地址。尽管不同任务可以访问相同的地址(比如0x00401010),但因为这个地址仅仅是本任务空间中的虚拟地址,不同任务处于不同的虚拟空间中,不同任务的虚拟地址可以被映射到不同的物理地址,这样就可以很容易防止一个任务内的代码直接访问另一个任务的数据。IA-32CPU提供了两种机制来实现内存映射:段基址和页机制。
##任务内保护
任务内保护主要用于操作系统。
操作系统的代码和数据通常被映射到系统中每个任务的内存空间中,并且对于所有任务其地址是一样的。例如,在Windows系统中,操作系统的代码和数据通常被映射到每个进程的高2GB空间中。这意味着操作系统的空间对于应用程序是"可触及的",应用程序中的指针可以指向操作系统所使用的内存。
任务内保护的核心思想是权限控制,即为代码和数据根据其重要性指定特权级别,高特权级的代码可以执行和访问低特权级的代码和数据,而低特权级的代码不可以直接访问和执行高特权级别的代码和数据。高特权级通常被赋予重要的数据和可信任的代码,比如操作系统的数据和代码。低特权级通常被赋予不重要的数据和不信任的代码,比如应用程序。这样,操作系统可以直接访问应用程序的代码和数据,而应用程序虽然可以指向操作系统的空间,但是不能直接访问,一旦访问就会被系统发现并禁止。
##特权级
IA-32处理器定义了4个特权级,又称为环(ring),分别用0,1,2,3表示。
* 描述符特权级别(DPL),位于段描述符和门描述符中,用于表示一个段或门的特权级别。
* 当前特权级别(CPL),位于CS和SS寄存器的位0和位1中,用于表示当前正在执行的程序或任务的特权级别。通常CPL等于当前正被执行的代码段的DPL。当前处理器切换到一个不同的DPL时,CPL也会随之变化。但有一个例外,因为一致代码段可以被特权级别与其相等或更低(数值上大于或等于)的代码所访问,所以当CPU访问DPL大于CPL(数值上)的一致代码段时,CPL保持不变。
* 请求者特权级别(RPL),用于系统调用的情况,位于保存在栈中的段选择子位0和位1,用来代表请求系统服务的应用程序的特权级别。在判断是否可以访问一个段时,CPU要检查CPL,也要检查RPL。这样做的目的时防止高特权级的代码替应用程序访问应用程序来本没有权力访问的段。举例来说,当应用程序调用操作提供服务时,操作系统会检查保存在栈中的来自应用程序的段选择子的RPL,确保它与应用程序代码段的特权级别一致。IA-32CPU专门设计了一条指令ARPL来辅助这一检查。而后,当才做系统访问某个段时,系统会检查RPL。此时如果只检查CPL,那么因为正在执行的是操作系统的代码,所以CPL反映的不是真正发起访问这的特权级。
以访问数据段为例,当CPU要访问位于数据段中的操作数时,CPU必须先把指向数据段的段选择子加载到数据段寄存器(DS、ES、FS、GS)或者栈寄存器(SS)中。在CPU把一个段选择子加载到段寄存器之前,CPU会进行特权检查。具体来说就是比较当前代码的CPL(也就是当前正在指向的程序或任务的特权级),RPL和目标段的DPL。仅当CPL和RPL数值上小于或等于DPL时,即CPL和RPL对应的权限级别等于或大于DPL时,加载才会成功,否则会抛出保护性异常。这样便保证了一段代码仅能访问与它同特权级或特权级比它低的数据。