一个进程的地址空间,从用户的角度看,是由若干的段(segment)组成的,这些段可以分为两种:私有段(private)、共享段(shared)。cpu也是按照用户的逻辑进行内存管理的(分段),Intel Pentium规定了每种段最多有8K个,每个segment最大4G。一个cpu对应有一个GDT(global descriptor table),该表详细描述了shared segment,这个表为所有进程共享的;一个process对应有一个LDT(local),该表详细描述了该process的private segment,这个表是进程私有的。
GDT/LDT的结构,每条记录有64bit:
0-11 (12bit) | 12-43 (32bit) | 44-63 (20bit) |
属性 | 段基址 | 段界限 |
硬件中有一个寄存器,叫做GDTR(registor),共48bit,如下:
0-31 | 32-47 |
指向GDT的开始位置 | 规定GDT的长度 |
注意:上面16bit规定了GDT的长度,该长度是指,从32bit的指针指向的GDT开始位置到GDT结束位置的绝对长度,这个长度不是表的长度,不是表的record数量。下面会说明这个绝对长度跟表的长度(表的record数量)的关系。
文章开头说了,Intel Pentium规定了每种段最多有8K个,也就是说每个GDT/LDT有8K(2^13)条record,GDTR中又规定了GDT的绝对长度是2^16,由此可以得出GDT中每个record长度为2^16 / 2^13 = 8Byte,共64bit,正好与上面GDT/LDT的结构中说明的相一致。
问题又来了,既然每个GDT/LDT可以有2^13条record,那么就需要一个至少13bit的Selector来确定是哪一条record了。事实也是这样的,一个逻辑地址包含两部分,如下:
0-31 | 32-47 |
段内偏移量 | Segment Selector |
由上可以发现,32bit的段内偏移量确实确定了每个段的大小最大为4G(如文章开头所述)。而Selector是16bit不是我们预测的13bit,事实上其结构如下:
0-12 | 13 | 14-15 |
指向GDT/LDT中的一条record | 确定GDT还是LDT | 操作权限 |
-----------------------------------------------------------------------------------------------------------------------------------
由以上介绍可知一个逻辑地址如何转化成为物理地址(确实可以得到一个32bit的地址,先就叫他物理地址吧,其实这个32bit的地址还要分页的)。为什么一个逻辑地址是48bit而不是32bit呢(如表“逻辑地址结构”所示)?我们在程序中如果打印一个变量的地址,确实就是一个32bit的数字啊,类似0x12345678,它并不是48bit的啊?!别忘了,我们的程序是处在一个个的Segment中的(如文章开始所述),无论是指令还是数据。我们打印出的地址其实是段内的偏移地址,程序的16bit段号由操作系统分配管理,我们是看不见的,但是的确存在。
假设一个进程需要1M的内存(指令加数据),那么os先会从LDT中选择一个段号(16bit),分给这个进程(在这个段内,我们有4G的空间可以发挥,但是我们只需要1M ^_^),如果进程有需要全局空间,os还会在GDT中为之分配的。因此..........cpu寻址的时候,也需要根据段号加段内偏移地址来找到物理地址。
注意注意注意:以上的解释中,为简单,没有说明分页!
总之,SegmentSelector(16bit) + 段内Offset(32bit) = 一个32bit的地址值
------------------------------------------------------------------------------------------------------------------------------------
下面我告诉你,我们要对刚刚得到的32bit地址值进行分页处理,下面所有的操作都是在一个Segment内的4G空间内进行的。分页是这样分的:
操作系统为每个Segment维护一张表,称作页表,结构如下:
0-19 | 20-31 |
页号 | 页内Offset |
0-9 | 11-19 | 20-32 |
一级页号 | 二级页号 | 页内Offset |
--------------------------------------------
综上所述,在程序中新申请一块空间,大致过程是这样的:
---->os在LDT中分配一个段号,在这个段里有4G的空间;
---->把这4G空间先分成1024份,每份大小1024 x 4K
---->把每一分再分为1024份,每份大小4K
---->把其中的一份或几份分配给申请者,并记录。
-----------------------------------------------------
注:本文是新手综合课本和论坛资料原创,为加深理解,只要求逻辑正确,没有拘泥于具体细节(如具体多少bit,以及bit顺序)。它只提供了一种os管理内存的方案,并不是所以os都是这么设计的,即使是采用的这种设计,具体实现细节也因os而异。