windows内存分段

1.3.2  Windows的内存管理html

在这一节中,读者能够解决初学Win32汇编的两个大疑问:编程

● Win32汇编中,每一个程序均可以用4 GB的内存吗?安全

● Win32汇编源代码中为何看不到CS,DS,ES和SS等段寄存器的使用?操作系统

1. DOS操做系统的内存安排设计

Win32编程相对于DOS编程最大的区别之一就是内存的使用。视频

先来回顾一下DOS操做系统的内存使用,如图1.1所示。DOS操做系统运行于实模式中,因为8086处理器的寻址范围只有1 MB,当时把系统硬件使用的内存安排在高端,地址是从A0000h(即640 KB)开始的384 KB中,其中有用于显示的视频缓冲区和BIOS的地址空间。而在内存低端,安排了中断向量表和BIOS数据区;剩下从500h开始到A0000h总共不到640 KB的内存是操做系统和应用程序所可以使用的;应用程序不可能使用这640 KB之外的内存。这就是著名的“640KB限制”。但即便在这640 KB中,DOS操做系统又占领了低端的一部份内存,最后剩下600 KB左右的内存才是应用程序真正能够用的。若是系统中有内存驻留程序存在,那么应用程序还要和这些TSR程序共同分享这段内存空间。htm

当80386处理器推出后,能够寻址的内存范围达到了4 GB,利用XMS驱动程序能够访问到全部的4 GB地址空间。但16位的段寻址方式限制了DOS程序,“可见”的内存范围仍是停留在00000h到FFFF0h+64 KB的范围内,全部高于1 MB的扩展内存只能经过XMS驱动程序当作数据交换使用,程序的执行空间并无什么增长。教程

2. 80386的内存寻址机制索引

Windows的内存管理和DOS的内存管理有很大的不一样,在了解Windows的内存管理模式以前,须要对80386保护模式下内存分页机制有些了解。为了作个对比,先来看实模式下的内存寻址方式,即DOS下的寻址方式,如图1.2所示。ip


图1.2  实模式下的内存寻址方式

在实模式下,一个完整的地址由段地址和偏移地址两部分组成。段地址放在16位的段寄存器中,而后在指令中用16位的偏移地址寻址。处理器换算时先将段地址乘以10h,获得段在物理内存中的起始地址;而后加上16位的偏移地址获得实际的物理地址。如xxxx:yyyy格式的虚拟地址在内存中的实际位置是xxxx×10h+yyyy。

当80386处理器工做在保护模式和虚拟8086模式的时候,可使用所有32根地址线访问4 GB大的内存。段地址加偏移地址的计算方法显然没法覆盖这么大的范围。但计算一下就能够发现,实际上和8086一样的限制已经不复存在,由于80386全部的通用寄存器都是32位的,2的32次方至关于4G,因此用任何一个通用寄存器来间接寻址,没必要分段就已经能够访问到全部的内存地址。

这是否是说,在保护模式下,段寄存器就再也不有用了呢?答案是否认的。实际上段寄存器更有用了,虽然在寻址上再也不有分段的限制问题,但在保护模式下,一个地址空间是否能够被写入,能够被多少优先级的代码写入,是否是容许执行等涉及保护的问题就出来了。要解决这些问题,必须对一个地址空间定义一些安全上的属性。段寄存器这时就派上了用途。可是涉及属性和保护模式下段的其余参数,要表示的信息太多了,要用64位长的数据才能表示。咱们把这64位的属性数据叫作段描述符(Segment Descriptor)。 (段描述 符中除了包含基址外,还包含其余安全上的属性)

80386的段寄存器是16位的,没法放下保护模式下64位的段描述符。如何解决这个新的问题呢?解决办法是把全部段的段描述符顺序放在内存中的指定位置,组成一个段描述符表(Descriptor Table);而段寄存器中的16位用来作索引信息,指定这个段的属性用段描述符表中的第几个描述符来表示。这时,段寄存器中的信息再也不是段地址了,而是段选择器(Segment Selector)。能够经过它在段描述符表中“选择”一个项目以获得段的所有信息。

既然这样,段描述符表放在那里呢?80386中引入了两个新的寄存器来管理段描述符表。一个是48位的全局描述符表寄存器GDTR,一个是16位的局部描述符表寄存器LDTR。那么,为何有两个描述符表寄存器呢?

GDTR指向的描述符表为全局描述符表GDT(Global Descriptor Table)。它包含系统中全部任务均可用的段描述符,一般包含描述操做系统所使用的代码段、数据段和堆栈段的描述符及各任务的LDT段等;全局描述符表只有一个。(note:因此只有一个限长,放在了GDTR中,这里的GDTR意思是全局段描述符表寄存器。)

LDTR则指向局部描述符表LDT(Local Descriptor Table)。80386处理器设计成每一个任务都有一个独立的LDT。它包含有每一个任务私有的代码段、数据段和堆栈段的描述符,也包含该任务所使用的一些门描述符,如任务门和调用门描述符等。(raynote:虽然每一个任务都有一个独立的LDT,可是LDTR即段描述符表寄存器只有一个。)

不一样任务的局部描述符表分别组成不一样的内存段(???),描述这些内存段(note:指局部描述符表所在的内存段,而不是任务私有段)的描述符当作系统描述符放在全局描述符表中。和GDTR直接指向内存地址不一样,LDTR和CS,DS等段选择器同样只存放索引值,指向局部描述符表内存段对应的描述符在全局描述符表中的位置(???全局描述符表中存放了局部描述符表所在内存段的描述符信息。毕竟描述符表自己也是要在内存中存储的,要有对应的描述符信息)。随着任务的切换,只要改变LDTR的值,系统当前的局部描述符表LDT也随之切换,这样便于各任务之间数据的隔离。但GDT并不随着任务的切换而切换。

看到这里,读者可能会提出一个问题,既然有全局描述符表和局部描述符表两个表,那么段选择器中的索引值对应哪一个表中的描述符呢。实际上,16位的段选择器中只有高13位表示索引值。剩下的3个数据位中,第0,1位表示程序的当前优先级RPL;第2位TI位用来表示在段描述符的位置;TI=0表示在GDT中,TI=1表示在LDT中。

以图1.3为例,在保护模式下,一样以xxxx:yyyyyyyy格式表示一个虚拟地址。单单凭段选择器中的数值xxxx根本没法反映出段的基址在哪里。对于这个地址,首先要看xxxx的TI位是否为0,若是是的话,则先从GDTR寄存器中获取GDT的基址(图中的步骤①),而后在GDT中以段选择器xxxx的高12位当作位置索引获得段描述符(步骤②)。段描述符包含段的基址、(note:虽然32位寄存器彻底能够直接表示一个地址,但保护模式下仍是会分段)限长、优先级等各类属性,这就获得了段的起始地址(步骤③);若是xxxx的TI位为1的话就更复杂了,这表示段描述符在LDT中,这时第一步的操做仍是从GDTR寄存器中获取GDT的基址(步骤1'),而且要从LDTR中获取LDT(note:指一个描述符表)所在段的位置索引(步骤2',note:全部的位置索引都是指在GDT中的);而后以这个位置索引在GDT中获得LDT段的位置(步骤3');而后才是用(note:段选择器中的)xxxx作索引从LDT段中得到段描述符(步骤4'),再以这个段描述符获得段的基址等信息(步骤5')。分这两种状况获得段的基址后(图中Result所示),再以基址加上偏移地址yyyyyyyy才获得最后的线性地址。

关于段描述符的格式定义,读者能够参考其余讲述保护模式的书籍。


图1.3 保护模式下GDTR,LDTR,全局描述符表,局部描述符表和选择器的关系

总结:

GDTR中的基址是直接指向内存地址,占32位。

每一个任务 有本身 独立的LDT,从LDTR中得到的是当前任务的局部描述符表所在内存段在全局描述符表中位置 索引。

每一个任务 的局部描述符表LDT有不少 条目 ,段选择器,在TI=1的状况下,前面 的12位是在ldt中的位置 索引 。

LDTR中第三位是作什么 用??

WIN32汇编语言教程:第01章 背景知识 · 1.3 必须了解的东西(2)
http://www.feiesoft.com/win32asm/win32asm-1-5.html

相关文章
相关标签/搜索