个人计算机主板上的内存条和一个ROM芯片构成主存储器,保存正在运行使用的指令和数据。处理器从主存储器读取指令,在执行指令的过程中读写数据。
主存储器是一个很大的信息存储库,被划分成许多存储单元。为了区分和识别各个存储单元,并按指定位置进行存取,给每个存储单元编排一个顺序号码,该号码称为存储单元地址(Memory Address)。现代计算机中,主存储器采用字节编址,即主存储器的每个存储单元具有一个地址,保存一个字节(8个二进制位)的信息,因此也称为字节可寻址,因为通过存储单元地址可以访问到一个字节信息。
对存储器的基本操作是按照要求向指定地址(位置)存进(写入,Write)或取出(读出,Read)信息。只要指定位置就可以存取的方式,称为“随机存取”。
计算机存储信息的基本单位是一个二进制位(bit),一个位可存储一位二进制数0或1,一般使用小写字母b表示。8个二进制位组成一个字节(Byte),常用大写字母B表示,位编号自右向左从0开始递增计数,分别为D 0 ~D 7 ,如图1-4所示。8086和80286字长为16位,由2个字节组成,称为一个字(Word),位编号自右向左为D 0 ~D 15 。IA-32处理器字长为32位,由4个字节组成,称为双字(Double Word),位编号自右向左为D 0 ~D 31 。右边最低位称为最低有效位(Least Significant Bit,LSB),即D 0 位;左边最高位称为最高有效位(Most Significant Bit,MSB),对于字节、字、双字分别指D 7 、D 15 和D 31 位。
图1-4 数据的位格式
主存储器需要处理器通过总线进行访问,称为物理存储器。物理存储器的每个存储单元有一个唯一的地址,这个地址就是物理地址(Physical Address)。物理地址空间从0开始顺序编排,直到处理器支持的最大存储单元。8086处理器只支持1MB存储器,其物理地址空间是0~2 20 -1,用5位十六进制数表示为00000H~FFFFFH。IA-32处理器支持4GB存储器,其物理地址空间是0~2 32 -1,用8位十六进制数表示为00000000H~FFFFFFFFH。
表达存储容量时常使用的单位及数量如表1-2所示。它们虽然借用了日常生活中的千、兆、吉等单位,但没有使用其10 3 的倍数关系,而是2 10 =1024的倍数关系(近似10 3 ,即1000)。有些产品(如硬盘、U盘)的生产厂商给出的容量采用10 3 倍数关系,请注意分辨。
表1-2 存储容量的常用单位
为了有效地使用存储器,几乎所有操作系统和核心程序都具有存储管理功能,即动态地为程序分配存储空间的能力。IA-32处理器内部包含存储管理单元,提供分段和分页管理机制,以存储模型形式供程序员使用主存储器。
利用存储管理单元之后,程序并不直接寻址物理存储器。IA-32处理器提供了3种存储模型(Memory Model),用于程序访问存储器。
(1)平展存储模型
平展存储模型(Flat Memory Model)下,对程序来说存储器是一个连续的地址空间,称为线性地址空间。程序需要的代码、数据和堆栈都包含在这个地址空间中。线性地址空间也以字节为基本存储单位,即每个存储单元保存一个字节且具有一个地址,这个地址称为线性地址(Linear Address)。IA-32处理器支持的线性地址空间是0~2 32 -1(4GB容量)。
(2)段式存储模型
段式存储模型(Segmented Memory Model)下,对程序来说存储器由一组独立的地址空间组成,这个地址空间称为段(Segment)。通常,代码、数据和堆栈位于分开的段中。程序利用逻辑地址(Logical Address)寻址段中的每个字节单元,每个段都可以达到4GB。
在处理器内部,所有的段都被映射到线性地址空间。程序访问一个存储单元时,处理器会将逻辑地址转换成线性地址。使用段式存储模型的主要目的是增加程序的可靠性。例如,将堆栈安排在分开的段中,可以防止堆栈区域增加时侵占代码或数据空间。
(3)实地址存储模型
实地址存储模型(Real-address Mode Memory Model)是8086处理器的存储模型。IA-32处理器支持这种存储模型是为了兼容原来为8086处理器编写的程序。实地址存储模型是段式存储模型的特例,其线性地址空间最大容量为1MB,由最大为64KB的多个段组成。
编写程序时,程序员需要明确处理器执行代码的工作方式,因为工作方式决定了可以使用的指令和存储模型。IA-32处理器支持3种基本的工作方式(操作模式):保护方式、实地址方式和系统管理方式。
(1)保护方式
保护方式(Protected Mode)是IA-32处理器固有的工作状态。在保护方式下,IA-32处理器能够发挥其全部功能,可以充分利用其强大的段页式存储管理以及特权与保护能力。保护方式下,IA-32处理器可以使用全部32条地址总线,可寻址4GB物理存储器。
IA-32处理器从硬件上实现了特权的管理功能,方便操作系统使用。它为不同程序设置了4个特权层(Privilege Level):0~3(数值小表示特权级别高,所以特权层0级别最高)。例如,特权层0用于操作系统中负责存储管理、保护和存取控制部分的核心程序,特权层1用于操作系统,特权层2可专用于应用子系统(数据库管理系统、办公自动化系统和软件开发环境等),应用程序使用特权层3。这样,系统核心程序、操作系统、其他系统软件以及应用程序可以根据需要分别处于不同的特权层而得到相应的保护。当然,如无必要不一定使用所有的特权层。例如,在PC中,Windows操作系统处于特权层0,应用程序则处于特权层3。
保护方式具有直接执行实地址8086软件的能力,这个特性称为虚拟8086方式(Virtual-8086 Mode)。虚拟8086方式并不是处理器的一种工作方式,只是提供了一种在保护方式下类似于实地址方式的运行环境。例如,Windows中的MS-DOS运行环境。
处理器工作在保护方式时,可以使用平展或段式存储模型;处理器工作在虚拟8086方式时,只能使用实地址存储模型。
(2)实地址方式
通电或复位后,IA-32处理器处于实地址方式(Real-address Mode,简称实方式)。它实现了与8086相同的程序设计环境,但有所扩展。实地址方式下,IA-32处理器只能寻址1MB物理存储器空间,每个段最大不超过64KB;但可以使用32位寄存器、32位操作数和32位寻址方式,相当于可以进行32位处理的快速8086。
实地址方式具有最高特权层0,而虚拟8086方式处于最低特权层3。所以,虚拟8086方式的程序都要经过保护方式确定的所有保护性检查。
实地址工作方式只能支持实地址存储模型。
(3)系统管理方式
系统管理方式(System Management Mode,SMM)为操作系统和核心程序提供节能管理和系统安全管理等机制。进入系统管理方式后,处理器首先保存当前运行程序或任务的基本信息,然后切换到一个分开的地址空间,执行系统管理相关的程序。退出SMM方式时,处理器将恢复原来程序的状态。
处理器在系统管理方式下切换到的地址空间,称为系统管理RAM,使用类似于实地址的存储模型。
不论是何种存储模型,程序员都采用逻辑地址进行程序设计,逻辑地址由段基地址和偏移地址组成。段基地址(简称段地址)确定段在主存中的起始地址。以段基地址为起点,段内的位置可以用距离该起点的位移量表示,称为偏移(Offset)地址。逻辑地址常借用MASM汇编程序的方法,使用英文冒号(:)分隔段基地址和偏移地址。这样,存储单元的位置就可以用“段基地址:偏移地址”表示。存储单元可以处于不同起点的逻辑段中(当然对应的偏移地址也就不同),所以可以有多个逻辑地址,但只有一个唯一的物理地址。编程使用的逻辑地址由处理器映射为线性地址,在输出之前转换为物理地址。
编写应用程序时,通常涉及3类基本段:代码段、数据段和堆栈段。
代码段中存放程序的指令代码。程序的指令代码必须安排在代码段,否则将无法正常执行。程序利用代码段寄存器CS获得当前代码段的段基地址,指令指针寄存器EIP保存代码段中指令的偏移地址。处理器利用CS:EIP取得下一条要执行的指令。CS和EIP不能由程序直接设置,只能通过执行控制转移指令、外部中断或内部异常等间接改变。
数据段存放当前运行程序所用的数据。一个程序可以使用多个数据段,以便于安全有效地访问不同类型的数据。例如,程序的主要数据存放在一个数据段(默认用DS指向)中,只读的数据存放在另一个数据段中,动态分配的数据存放在第3个数据段中。使用数据段,程序需要设置DS、ES、FS和GS段寄存器。数据的偏移地址由各种存储器寻址方式计算出来(详见第2章)。
堆栈段是程序所使用的堆栈所在的区域。程序利用SS获得当前堆栈段的段基地址,堆栈指针寄存器ESP保存堆栈栈顶的偏移地址。处理器利用SS:ESP操作堆栈数据。
逻辑地址的段基地址部分由16位的段寄存器确定。段寄存器保存16位的段选择器(Segment Selector)。段选择器是一种特殊的指针,指向对应的段描述符(Descriptor),段描述符包括段基地址,由段基地址就可以指明存储器中的一个段。段描述符是保护方式引入的数据结构,用于描述逻辑段的属性。每个段描述符有3个字段,包括段基地址、段长度和该段的访问权字节(说明该段的访问权限,用于特权保护)。
根据存储模型不同,段寄存器的具体内容也有所不同。编写应用程序时,程序员利用汇编程序的命令创建段选择器,操作系统创建具体的段选择器内容。如果编写系统程序,程序员可能需要直接创建段选择器。
平展存储模型下,6个段寄存器都指向线性地址空间的地址0位置,即段基地址等于0,偏移地址等于线性地址。应用程序通常设置两个重叠的段:一个用于代码,一个用于数据和堆栈。CS段寄存器指向代码段,其他段寄存器都指向数据段和堆栈段。
当使用段式存储模型时,段寄存器保存不同的段选择器,指向线性地址空间不同的段,如图1-5所示。某个时刻,程序最多可以访问6个段。CS指向代码段,SS指向堆栈段,DS等其他4个段寄存器指向数据段。段式存储管理的段基地址和偏移地址都是32位,段基地址加上偏移地址形成线性地址。
实地址存储模型的主存空间只有1MB(=2 20 字节),仅使用地址总线的低20位,其物理地址范围为00000H~FFFFFH。实地址存储模型也进行分段管理,但有两个限制:每个段最大为64KB,段只能开始于低4位地址全为0的物理地址处。这样,实地址方式的段寄存器直接保存段基地址的高16位,只要将逻辑地址中的段地址左移4位(十六进制一位)加上偏移地址就得到20位物理地址。
图1-5 段式存储模型
32位Windows操作系统工作于保护方式,使用分段和分页机制,为程序构造了一个虚拟地址空间。尽管虚拟存储管理比较复杂,但对Windows应用程序来说,所面对的是0~FFFFFFFFH的4GB虚拟地址(线性地址)空间。在这4GB空间中,高2GB属于操作系统使用的地址空间,应用程序使用0~7FFFFFFFH的2GB线性地址空间,图1-6所示为Win32进程的地址空间分配情况。例如,32位Windows应用程序通常从00400000H开始分配地址空间。
图1-6 Win32进程的地址空间分配情况