之前的程序都是在实模式下面的程序,只能访问1M的地址,2^20
intel退出32位cpu完全兼容16位cpu,重要的一点是可以继续使用16模式下的寻址方法。
32位cpu地址线为32根,最大内存为4G,如何利用原来的seg:offset表示方式,来表示32根地址线。
把32位的物理内存写入一个表中,每个表项记录内存的开始地址和大小,边界,
16位的寄存器用来表示表的索引。比如:当16bit段寄存器中存放的数值为2,则表示内存放在表的第二个表项中。
事先分好的内存段信息存入一张表格中,让后段寄存器中表村你要访问的内存段所在的这张表的索引。
这张表包含的内容,如何通过这张表找到所需的内存块。
段选择子16位(段寄存器16bit)其中高13bit用来表示描述符的索引,低三位用来表示段描述符
表中所指向的段描述符的属性。(所以段描述符有2^13=8096个索引项)
15
TI表示从全局描述符表读取描述符还是从局部描述符中读取描述符。(进程的私密空间或者一些通用库的内存地址)
RPL,用于特权检查
没有开启分页机制,线性地址空间等于物理地址。(有了分页机制,比如ARM中的mmu,可以将多块线性地址映射到同一块物理地址,这个时候,线性地址远远大于物理地址,换入换出机制)
段寄存器存放了段描述符表中的索引号,通过该索引可以知道该索引号的段描述符
因为段描述符内记录了段在32位内存的开始地址和段的长度(段界限)那么就能定位
到实际的物理内存地址。
段描述符有8个字节,每个字节的具体含义。
段属性
段属性位于段描述符第六和第七个字节,用来描述该段是数据段还是代码段或者是堆栈段,对于数据段或者堆栈段来说是否可读或者可写(RW),对于代码段来说是否可执行||以及段描述符所指定的内存段在物理内存中是否存在。
type:4bit,描述该段是数据还是代码段,可读还是可写还是可执行??
DT:我们代码中一般式存储段
P:
AVL:intel保留,不管,都置为0
D:0表示16bitcode段,1是32bit code段,如果是数据段,0表示16bit地址,1表示32bit地址
G:这里是保护模式,G=1
由于我们在裸机上编写程序,因此我们必须在4G的内存空间中自己分配代码段和数据段。也就是说这个过程中没有操作系统的干涉,完全是我们自己搞定,想咋整就咋整。
在4G的内存范围内进行分配:
怎么把8M的数据填充到数据段描述符和代码段描述符
由于8M等于2^23(800000H),没有办法吧23bit的地址填充到20bit的界限中,通过段界限公式来转化:
段界限=limit*4k+0fffh,当limit=8M的时候,limit等于7ffh,limit=(800000H-0FFFH)/4k= 7FFh,4k这里转化为十六进制,为1000h,将7ffh这个数据填充到段描述符的20bit界限位中,不足的添零。
DPL表示内存段的权限,表示有一定权限的进程才能访问,0为最高级别,内核程序最高。
avl:intel保留位,设为0.不管
D:数据段,表示边界是4g还是64k,代码段:0表示16位代码,1表示32位代码
G:保护模式下为1,实模式下面为0
总共有64bit8byte,段基址为32位,4byte,分成两个部分,段属性为4byte,段界限为byte0,1,按位填充即可。
代码段描述符和数据段描述符类似,只是段的基址不一样,为800001H,第七个字节的属性不一样。
其中绿色部分是代码段和数据段不同的地方,第一个是byte5低四位为1010,表示的意思是,当s=1即bit44表示为数据段和代码段描述符时,byte5第四位为代码段,表示代码段可执行,可读,详细见自己动手写操作系统p48.第二个变化是段基址从800001h开始,因为数据段大小为8M=800000h,byte0还是不变。
上面就构造好了数据描述符和代码描述符。