上犹电脑信息网我们一直在努力
您的位置:上犹电脑信息网 > 文件问题 > 菜鸟windows下写个操作系统——实践(5)初学保护模式-windows文件保护

菜鸟windows下写个操作系统——实践(5)初学保护模式-windows文件保护

作者:上犹日期:

返回目录:文件问题

保护模式 这个概念实在太大了,想学得好得看Intel白皮书,主要还特别重要,操作系统的很多机制都需要硬件的支持,这就必须要对保护模式特别精通,目前我就想写个简单的玩具版操作系统,只会去了解我目前能用到的东西。

先将完成效果图发出来,然后再一步步解释:

菜鸟windows下写个操作系统——实践(5)初学保护模式

菜鸟windows下写个操作系统——实践(5)初学保护模式

菜鸟windows下写个操作系统——实践(5)初学保护模式

实模式和保护模式

实模式下,用户程序对内存的访问非常自由,没有限制,想修改哪个内存单元就修改哪个内存单元。

保护模式下,主要的功能我认为就是保护,保护内存的安全,保护代码的安全,不要随意让别人访问,随意修改使用,这就需要在某些内存段上面加一些“约束条件”,比如一段地址空间,一段代码空间,一段堆栈空间,我想让谁有权限访问这段空间,我就给谁一个权限,由于段会很多,明显用一个寄存器无法放下,我们就需要在内存空间里写一张表,把这些信息放进那个表里,全局描述符(GDT)表就由此而来。

既然叫表了,那里面肯定就有表项,表项被称为段描述符,大小为64位(8个字节),里面包含了内存段的起始地址,大小,安全属性等等,这个表一般会稍微有点大,寄存器是肯定不行了,那只能放在内存中,然后让CPU里的GDTR(48位)寄存器指向这个内存空间,在实模式和保护模式下,对内存的访问仍使用段地址+偏移地址,但不同的是,保护模式下每个段必须先登记在GDT表中,当你访问的偏移地址超出段的界限时或者权限不够时等等,CPU就会阻止这种访问,并产生一个内部异常的中断。

每个段都需要一个段描述符,8个字节组成,多个段描述符表像数组一样的挨着形成全局描述符表(GDT),既然是全局的,那他就是为整个软硬件服务的,所以在进入保护模式前,就必须定义好全局描述符表。上图,边界为16位,就是2的16次方=65536字节,而一个段描述符点8字节,所以32位下,GDT表总共可以有8192个段描述符。

在这里我们要先确定:1.在进入保护模式前,GDT表就必须被定义。

2.GDT表可以放在内存的任何一个地方,但因为我们是在实模式下进入保护模式的,所以最开始实模式下也只有1M的地址空间,我们放GDT表也只能放在1M地址以内,不过进入保护模式后也可以重新定义GDT。

不知道各位有没有想到一个问题,既然GDT表是放在内存中的,而且会频繁的使用,那CPU怎么受得了访问内存这么慢的速度,而且由于兼容的关系,段描述符里的信息是比较杂乱的,还得重新拼合起来也需要时间,所以从80286保护模式开始后,硬件层面使用了对段寄存器的缓冲技术,对段寄存器进行缓冲,这就是段描述符缓存寄存器,当然这个我们程序员是看不到的,当CPU将段描述符信息重新整合起来放进段描述符缓存寄存器,以后访问相同的段时,通过段寄存器直接找段描述符缓冲寄存器。

顺便提个醒,32位的CPU用在16位实模式下,也是有这个段寄存器缓冲技术的。

详解全局描述符表(GDT)

G---粒度,为0时,段界限以字节为单位,界限大小为1B~1MB,因为段界限是20位的。

为1时,段界限以4K字节为单位,界限大小为4KB~4GB。

S---描述符类型,为0时,表示系统段。

为1时,表示代码段或者数据段。

DPL---描述符的特权级,0~3总共4个特权级,0最高,3最低。

P---段存在位,为0时,段不存在。

为1时,段存在。

D/B---默认的操作数大小,如果是代码段,这里就是D位,D=0表示偏移地址或者操作数是16位的;D=1表示32位的。

如果是栈段,这里就是B位,B=0表示使用SP,B=1表示使用ESP

L------64位代码段标志,保留给64位使用,目前置为0.

TYPE---描述符的字类型

上图的数据段X代表是否可执行,E位指示数据扩展方向,E=0是向上扩展的普通数据段,E=1是向下扩展的栈段,这个向下扩展的栈堆目前很少用

目前操作系统用的栈段就是用的普通数据段,至于他每次从高地址往低地址走,是由push和pop的机制引发的,而非是这里设置造成的,

W指示段的读写属性,W=0时,不能写,W=1可以写,A是已访问位,最近有访问时,CPU自动置1,对该位的清零是由软件或者操作系统负责的。

注:这个A位在内存管理时比较重要,它会配合P位,将使用频率较低的段换出到硬盘

代码段C---是否是特权依从段,C=0,非依从(只能相同权限的代码段才能调用)。C=1依从关系(允许低特权转移到高特权级)

R---是否可以读出,R=0表示不能读出。R=1表示可以读出。(CPU总可以读出,是用来限制程序和指令的行为)

AVL----软件使用的位,目前好像没什么用,预留给操作系统,CPU用不到。

这么多理论的玩意,看着就烦,那还是根据完成的图里来一一对应吧。

上面说了,在进入保护模式前,必须要先定义GDT表,那我们就先来定义一下:

还是老样子先引入头文件"boot.inc",设置他的偏移地址,这里我把LOAD_BASE_ADDR放入了头文件,是0x900,方便以后修改

%include "boot.inc"

section load vstart=LOAD_BASE_ADDR

jmp program_start ;因为下面这段空间我们用来定义变量和常量,CPU不能当代码段来运行,所以我们直接跳过

现在开始来定义GDT表了

CPU规定,GDT中的第一个段描述符必须是空描述符,或者称为NULL描述符,为了防止使用未初始化的数值。

GDT_BASE:

dd 0x00000000

dd 0x00000000

所以高32位和低32位我们都设置成0

目前我们规划能用到三个段,都是0环的代码段、数据段、显存段,以后功能多了再添加

第二个我们就设计成了0环的代码段:

CODE_SEGMENT:

dd 0x0000FFFF ;低32位,

dd 0x00CF9800;高32位

现在我们可以根据上面的段描述符表来填空了:

1。我后续的代码段的基地址是0,那我就在段基地址的位里面全部填0。

2。因为后面我会开启分页,所以这里界限我设置4G(0xFFFFFFFF),那段界限的位数都填1,但这样子段界限也只有20位,对了,在粒度G的位置填1,这样就是以4K为单位来计算:(0xFFFFF+1)*4096-1=0xFFFFFFFF

3。S=1 设置为代码段或数据段,TYPE=1000,非一致性代码段,代码不能被程序读出,访问为0,DPL=00,我们需要0环权限 ,p=1代表段存在 D/B=1,表示32位,L=0,AVL=0。

填空完成,就是这么简单,So easy!

数据段同上,只是个别位置要改变,TYPE那里变动一下,别的基本上没有什么变化

DATA_SEGMENT:

dd 0x0000FFFF

dd 0x00CF9200

麻烦的是显存段,因为我们的显存的基地址是0xB8000,长度是32K,所以我们需要在基地址和段界限那里做出更改,最后修改后的值为:

VIDEO_SEGMENT:

dd 0x80000007

dd 0x00C0920B

未完待续,写这玩意太费时间!

菜鸟windows下写个操作系统——实践(5)初学保护模式

相关阅读

关键词不能为空
极力推荐

电脑蓝屏_电脑怎么了_win7问题_win10问题_设置问题_文件问题_上犹电脑信息网

关于我们