Intel Architecture下的内存管理
---从Intel CPU这个角度看保护模式下的内存管理
-
摘自:http://perdubug.go.nease.net/htmls/note/memory_management.htm
-
进程和线程:http://perdubug.go.nease.net/htmls/theory/multitasking.htm#
-
Win32环境下的堆栈:http://perdubug.go.nease.net/htmls/win32/win32stack.htm#
-
2004.8.31晚上,阅读了Bill Blunden写的一本电子书<<Memory Managment Algorithms and Implementation in C/C++>>,一开始以为是一本写C++方面的书所以错误的把它
-
归到我的C++目录下了,这本书的第一篇写的是内存管理机制,写得非常好,老实说其中涉及的一些内容我应该在几年前阅读完Stefano Maruzzi写的<<Windows95开发
-
人员指南>>的头几章的时候就应该明白的,可惜那本书的翻译让我实在是看不明白,今天有幸看到了这本书使得我不上了自己在这一块的知识缺口,万幸,万幸...
-
概括一下先:要明白内存管理应该是很不容易的,我能想象出来的途径有两个:从硬件角度去了解,看看硬件都提供了些什么样的机
-
制来支持内存管理;从操作系统角度去了解,看看操作系统是怎么利用硬件提供的机制来实现自己的内存管理的;我草草写的这个文
-
章主要是想让自己从硬件角度先对内存管理有一个正确的认识和了解...
-
-
Intel Architecture既支持对内存的直接物理寻址也支持虚拟内存(通过页机制),在物理寻址模式下线性地址就相当于是物理地
-
址,在页机制下所有的代码,数据,堆栈,系统段(segment),GDT(全局描述符表),IDT(中断描述符表)都可以被分页,只有最近被
-
访问的页才会滞留在物理内存中。物理内存中的页的位置(在Intel架构下这种页被称为Page Frame)由两种系统数据结构来标示:一
-
个页目录,一堆的页表(注意,页目录和页表会始终留在物理内存中的):
-
1、页目录中的某一个条目中包含的信息包括:一个页表的物理基址,存取权限以及其他一些内存管理信息。
-
2、页表中的某一个条目中包含的信息包括:一个Page Frame的物理地址,存取权限以及其他一些内存管理信息。
-
那么页目录的基址存在哪呢?存在CR3寄存器中,这是一个系统寄存器。为了使用页机制,一个线性地址被分成三个部分:
-
1、该地址所指向的数据在某个Page Frame中的偏移地址;
-
2、上面提到的那个Page Frame在页表中的偏移地址;
-
3、上面提到的那个页表在页目录中的偏移地址;
-
上面这样说肯定很绕,看看下面这幅图就明白Page Frame、页表、页目录三者之间的关系了了:
-
-
A.把相关基本概念都摊开看个清楚
-
-
什么叫描述符表(Descriptor Table)?
-
它类似于一个数组,数组的每一项是一个段描述符(Segment Descriptor);
-
-
什么叫段描述符(Segment Descriptor)?
-
段描述符描述了一段内存(segment)的属性,包括段的大小、存取权限、段的特权级别、段的类型(数据段,代码段...)、段的基址等;
-
按照Intel 的说法:A segment descriptor provides the base address of a segment and access rights, type, and usage
-
information.每一个 段描述符有一个段选择器(Segment Selector)和它关联,段描述符(Segment Descriptor)有64-bit长,其中值
-
得注意的字段是:
-
0-15 指明了这个段描述符所描述的那块内存(Segment)的大小的限制,最大应该是2的16次方=64K
-
16-31 指明了这个段描述符所描述的那块内存(Segment)的基址(16-bit)
-
SS 0 系统段,1 代码或数据段
-
type 4-bit的组合说明这个段的类型,如下:
-
-
type=Accessed表明这个segment刚刚被访问过
-
type=Expand down 则这个segment很适合做stack,因为它从高地址向低地址生长
-
type=Conforming 则这个segment允许低权限的的代码段跳到这来在更低的权限下执行它们的代码
-
-
什么叫段选择器(Segment Selector)?
-
段选择器是一个段的唯一标示,其中的信息包括: 段描述符在GDT或者是LDT中的索引、标志位、存取信息;一个段选择器和一个偏移
-
地址唯一的确定了一个段(Segment)中的某一个字节,为什么这么说呢,因为一个段选择器可以确定一个段描述符,段描述符中包含了
-
段的基址,有了基址和偏移地址当然就可以唯一确定段中的一个字节了。
-
-
什么叫段(Segment)?
-
段是从程序角度看不同的地址空间,segment在Intel正式文档中的定义如下:
-
1.This is a form of addressing where a program may have many independent address spaces, called segments.
-
2.segmentation provides a mechanism for dividing the processor’s addressable memory space (called the linear
-
address space) into smaller protected address spaces called segments.If more than one program (or task) is
-
running on a processor, each program can be assigned its own set of segments.
-
从此可以看出的信息有:
-
1、段是受保护的,它提供了一种机制使得各个程序间不会相互侵犯对方的地址空间;
-
2、段是从程序的观点来划分的;
-
-
段式管理(Segmentation Mechanism)和页式管理(Paging Mechanism)
-
这是Intel Architecture在保护模式下提供的两种内存管理机制,段式主要是为了解决各个程序/任务之间相互不侵犯的问题,使得各个
-
程序/任 务在自己的 小天地里面活动,页式管理主要是为了解决内存需求和实际的物理内存之间的平衡问题;在Intel Architecture的
-
保护模式下,段式管理是必须的,但是页式 管理是可选 的,也就是说通过一些设置可以取消页式管理机制。内存分段结构是Intel CPU
-
的典型特性,其它处理器不一定具备这样的特性,比如RISC,也就是说内存分段将是程序在不同平台之间进行移植所需要面对的一个问题。
-
-
线性地址(Linear Address)和逻辑地址(Logical Address)
-
All of the segments within a system are contained in the processor’s linear address space. To
-
locate a byte in a particular segment, a logical address (sometimes called a far pointer) must be
-
provided.
-
从此可以看出的信息有:
-
1、线性地址是用来在所有的段组成的地址空间中寻址的,而逻辑地址是用来在一个段内寻址的,既然逻辑地址是在段内寻址那么它必
-
须包含一个段选择器和一个偏移;
-
-
物理地址(Physical Address)
-
按照Intel的正规说法:The physical address space is defined as the range of addresses that the processor can generate
-
on its address bus.
-
-
虚拟内存(Virtual Memory)和页机制(Paging Mechanism)
-
页机制是支持虚拟内存的底层保障
-
-
实模式和保护模式
-
实模式
-
保护模式
-
由此可以看出,实模式的管理规则比较简单,在实模式中寻址空间被限制在1 MB以内,实模式没有提供任何一种内存保护机制,如果
-
想实施一个有效的多任务运行环境,内存保护不可缺少。实模式和保护模式的根本区别在于访问内存位置的 机制不同。
-
-
TLB(Translation Look-aside Buffer)
-
TLB相当于是一个cache,目的是为了减少访问页目录、页表的次数,它一般会做在CPU芯片里面,大小一般为64K。
-
-
-
-
B.Intel Architecture在保护模式下的内存管理主要包括:段管理机制和页管理机制
-
-
我们可以看到逻辑地址由两部分组成:段选择器和偏移量;上面我们已经说了段选择器就像是索引使得我们可以在GDT中找到一个段描述符,
-
通过这个段描述符我们又可以找到线性地址空间中的某一个段,知道了这个段加上逻辑地址中的那个偏移量,我们就可以得到一个线性地址;
-
根据这个线性地址结合分页机制我们又可以定为一个物理地址,妙,太妙了~~~~
-
从上面这幅图我们可以看出很多信息:
-
1、线性地址空间和物理地址空间的区别和关系;
-
2、逻辑地址是面向线性地址空间的;
-
3、逻辑地址到线性地址空间的转换是通过GDT, Segment Selector, Offset来完成的;线性地址空间到物理地址空间的转换是通过页目录,
-
页表,Page Frame来完成的,但是请注意,前面这句话是有前提的:如果系统采取页机制的话,因为我们前面提到了页机制是可选的,如果
-
系统不采用页机制的话那么线性地址到物理地址的映射就是直接的,赤裸裸的。
-
但是我个人认为这幅图也有一个需要斟酌的地方,那就是最右边那个Page确切地说应该是Page Frame。
-
-
页目录的描述如下,其中一些字段值得了解
-
1.P 由操作系统来维护的,用来表明此页是否在内存中
-
2.PS 页大小
-
-
至于是否采用页机制由系统寄存器CR0决定,CR0中的PG字段决定了是否采取页式管理,如果没有设置则32-bit地址被当作物理地址对待
-
1.The CR0 is used to control the processor mode and state of the processor
-
2.The CR1 is reserved
-
3.The CR2 register is used to store the linear address that has caused a page fault.
-
4.The CR3 plays a central role in the resolution of physical addresses when paging has been enabled
-
5.The CR4 register is used to enable a couple of advanced mechanisms.比如控制页的大小
-
-
C.Basic Flat Model and Protected Flat Model
-
基于Intel Architecture提供的段管理基址,可以在上面构建各种各样的具体内存管理模式:
-
-
1.Basic Flat Model
-
-
至少需要两个段选择器:一个指向数据段,一个指向代码段;这两个段都指向同一个4G大小的线性地址空间;
-
-
2.Protected Flat Model
-
-
-
D.Intel Architecture提供的四个用于内存管理的寄存器
-
-
先看看在内存管理方面Intel CPU给我提供了什么样的支持,Intel Architecture提供了四个用于内存管理的寄存器[1]:
-
a. GDTR 全局描述符表的寄存器
-
b. IDTR 中断描述符表的寄存器
-
c. TR 任务寄存器
-
d. LDTR 局部描述符表的寄存器
-
-
A. GDTR(全局描述符表的寄存器)
-
由两部分组成:一个32-bit的地址和一个16-bit的表长限制描述,32-bit地址指向GDT,后面的16-bit用来说明这个GDT的长度限制;
-
Intel提供了两条指令来对此寄存器进行操作:LGDT,SGDT.
-
B. LDTR(局部描述符表)
-
由两部分组成:一个16-bit的段选择器、一个32-bit的地址、一个16-bit的段长限制描述和LDT的属性信息,32-bit地址指向LDT,
-
Intel提供了两条指令来对此寄存器进行操作:LLDT,SLDT.
-
因为LDT是存在于某一个segment中的,所以势必在GDT中有一个段描述符来描述此块内存。
-
-
E.用一张藏宝图来说明上面的一切
-
-
F.留给自己的问题
-
具体到windows,它在Intel Architecture提供的那些机制上具体采用了什么样的内存管理机制呢?
-
-
1.Flat Address Space(平址)
-
Windows NT中只采用了一个4G的段,也就是说操作系统、程序都在这一个4G的线性地址空间中,然后用分页机制解决线性地址空间和
-
物理内存之间的平衡问题;在这4G空间中,低2G为用户地址空间,高2G为kernel address space;
-
这个4G怎么来的呢?我以前就知道在32-bit的WinNT环境下下CS,DS,ES,SS四段合一,Segment:Offset中只有Offset有用,32位的Offset
-
能寻址4G空间,解答完毕。
-
-
2.Process Isolation(进程隔离)
-
WinNT通过为每一个进程维护一个页目录(Page Directory)的办法来实现进程隔离,妙吧;但是有一个问题:那kernel部分呢?进程如何访
-
问这些kernel部分?在WinNT中,kernel部分被放在kernel page中,这些页是一种特殊页(supervisor pages),它们不在前面提到的页目
-
录所能访问到的那些属于进程的页范围内,这样就可以避免用户模式的代码(User Mode Code)访问kernel。
-
下面这幅图是微软的Windows 2000技术书刊上为了说明进程隔离画的图:
-
-
3.Code Page Sharing in DLLs(动态库)
-
在WinNT中,如果进程M装载了动态库X.DLL(装载后返回的线性地址为A1),进程N也装载了X.DLL(装载后返回的线性地址为A2),如果
-
A1=A2那么A1,A2所指向的物理地址是同一个物理地址;也就是说动态库是放在共享空间中的,而且是只读的以防止被应用程序修改。
-
-
4.Context Switching(上下文切换)
-
The act of stopping a thread at the end of its time slice and scheduling another thread is called context switching.
-
上下文切换的开销是显而易见的:刷新TLB的开销;如果两个线程属于同一个进程的话那么TLB还能帮助我们节省一点开销,因为此时TLB还用得
-
着,但是如果两个线程属于不同的进程那么TLB里面Cache的那些线性地址就没有用了(因为它属于另外一个进程的地址空间),也就是说此时TLB
-
就起不了作用了,这时候就需要刷新TLB了。在上下文切换是需要保存和重载下列数据:
-
1.程序计数器;
-
2.处理器状态寄存器;
-
3.其他寄存器的内容;
-
4.User和Kernel栈的指针;
-
5.指向页目录的指针,因为每一个进程有自己的页目录所以切换时要保留自己的那个指针;
-
-
参考书目:
-
[1] -- Intel Architecture Software Developer's Manual Volume 3:System Programming Guide(1997)
-
[2] -- Bill Blunden, Wordware Publishing, Inc. ,<<Memory Management Algorithms and Implementation in C/C++>>
-
[3] -- Randy Kath,December 21, 1992,The Virtual-Memory Manager in Windows NT