内核第二讲,内存保护的实现,以及知识简介,局部描述符,全局描述符.

        内核第二讲,内存保护的实现,以及知识简介,局部描述符,全局描述符.

一丶了解80386的各种模式

80386,也就是32位系统下,有三种模式需要了解一下.

实模式,保护模式.虚拟86模式

实模式: 指的是操作系统在启动的是否,这时候访问的内存都是实际的物理内存.而在这个是否,操作系统会填写内核中的内中表格.比如今天讲的表(全局描述符表  GDT)

保护模式: 当各种表填写好了,那么我们的内存也被保护了.这个是否我们的进程就不会直接访问物理地址了.进而产生了保护行为,我们的内存就有了 可读 可写,可执行一说了.

虚拟86模式: 操作系统启动的是否,运行的都是实际的16位汇编.那么现在我们假设有一个16位程序要启动.那么修改了我们物理地址的内存,那么保护模式不就没用了.所以为了防止这一情况的产生,操作系统做了一个虚拟86模式,也就是说可以运行16位汇编程序.

关于更详细的介绍,请下载当讲课堂资料的  "8086处理器的工作模式.doc"文件进行查看.

二丶保护模式如何保护内存的.

我们通过上面模式的介绍,知道的操作系统启动的是否会从实模式启动,然后会切换到保护模式.

那么是如何保护内存的.

比如我们要保护 100地址的内存,让其支持可以读,不可以写.那么在 ring3下有汇编代码

mov [100],10  

如何保证它不被改写.

思路一:

  操作系统作者进行各种判断,判断这个地址不可以写.

思路二:

  留下接口,比如我们编写SDK程序的是否,当鼠标点击的是否,会产生一个回调函数,那么这个回调函数是用户写的.因为当鼠标按下,操作系统只能通知你,但是不知道你要怎么做.

思路三:

  做表,通过查表,进而判断内存是否可以访问或者读写.

这里关于前两种思路,第一种思路,对于操作系统肯定不现实的.每次访问内存都要判断,校验,不说你能不能完成,就算真的完成了,那么你的操作系统也会奇卡无比.

思路二,思路二放在我们自己编写的是否倒是可以.但是对于操作系统来说也是不现实的.

思路三,这个可以了.我们可以通过查表的方法,进行判断.而且只在访问内存的是否才进行判断内存是否可读写.

对于上面来说,我们就会产生一个新的疑问.这个表怎么做才合理.

在做表之前,我们要熟悉汇编中的段寄存器. 我们知道,在16位汇编中,我们可以通过段+偏移的方式来寻找内存.管理内存.那么我们现在要对内存做管理.那么就要分段了.

三丶分段管理概要

进行分段管理,来管理内存.那么我们应该怎么分.在分段之前我们要理解几个基本的概念.

1.逻辑地址.什么是逻辑地址

2.线性地址.什么是线性地址.

3.物理地址.

4.虚拟地址(暂时不讲)

逻辑地址: 逻辑地址指的是 段 + 偏移的方式,我们程序中每一行代码都是逻辑地址.

物理地址: 物理地址就是内存条的地址,也就是我们说的实际地址.

线性地址: 线性地址我们可以理解为逻辑上连续的物理地址.

段+偏移 查表,会查到线性地址.(物理地址),每个段+偏移都会查到一块物理地址.

比如我们的逻辑地址:  00401000~00402000,在逻辑上我们看的是连续的,但是通过查表转换为物理地址的是否则不是连续的.

看图:

有可能进过查表,得出的物理地址不是连续的.但是逻辑地址是连续的.

线性地址,如果我们没有虚拟内存.那么查到的线性地址就是物理地址.如果有虚拟内存,那么可能还要查表才能转化为物理内存.

为什么要查表得到物理地址?

  原因是,我们要对进程做隔离,对内存做保护.所以我们查到对应的物理地址其实是对它做一个保护.

四丶表格式怎么做.怎么做表?

既然上面我们明白了,要对内存做保护,那么首先要分段,对每一段内存做保护.那么该怎么设计.

1.段起始地址

2.大小

3.结束地址.

4.当前内存的保护属性.

我们会这样设计表格.其实我们想到这样设计,那么inter等等CPU也不是神,也会这样设计.

看下Inter设计的表格.

每错,第一次看到就会晕.那么仔细讲解这个存储段描述符表

首先我们理解一下 Base Address,也就是段.

段是实现虚拟地址到线性地址(物理地址)的转化机制的基础,也就是查表需要用到段.

那么在保护模式下.每个段有三个参数. 段基地址(Base Address) 段界限(limit),可以理解为区域

段属性(Attributes) 这三个字段则是inter定义的表格.和我们的差不多.  

Base Address:  段基地址,也就是我们分段的是否,在32位下,段基地址长32位.也就代表的我们可以分配4G个段.这些了解即可.

段界限(Limit): 段界限,指的就是当前内存起始位置加上当前段界限的大小,是属于什么属性的.这个在段属性中有表明.段界限用20位来表示,注意,是位.段界限可以是以字节为单位,也可以以4K为单位.什么意思那? 也就是说.这个段界限最大是20位表示  那么界限就是 2^20次方-1*4096字节大小

计算得出:

  2^20-1 * 4K  = FFFFF000,然而在32为系统中不是4G大小的界限.所以进而加上0FFFH大小.

公式: LIMIT = limit(2^20) * 4k + 0FFFh.  也相当于 左移<<12位,+0FFFh.

而是否*4K则看属性中的G位,如果G位为1,那么就*4K,如果为0,则*8位,也就是按照一个字节的界限去做.

4.1 线性地址的范围怎么计算.

base Address + limit 则可以形成一块内存区域.

假设段A的基地址是  00012345h,段界限为5678h,并且是以字节为单位(G = 0),那么

段基地址+ 段界限 = 000179BDh, 那么在00012345h ~ 000179BDH则是段A的线性地址

计算就是  段基地址 + 段界限 (也可以理解为,起始地址+一块区域.则这块区域就是线性地址)

如果属性中 G位 = 1,那么就是按照4K去计算.

0012345h + (5678 *4K)  + 0FFFh = 0568b344h

那么段A的线性地址就是从 0012345h ~ 0568b344h

我们上面计算线性地址,提供了一个表格.这个表格相信大家现在也能看懂了.

其中,我们知道了 段基地址占4个字节(32位)而段界限占20位,那么还属性则占12位,而一种表的大小是8个字节.

看上面的图,我们发现

M+7 存储的是段地址

M+4,M+3,M+2 这三个字节也是存储的段的地址.这是为什么? 因为inter为了兼容80286(24)位,那么只能添加新的东西来了,而在80286上面,是没有多加的.

M+1 M+0 这两个字节是16位,存储的是段界限,而M+6的8位也是存储的段界限的.

而M+5,M+6中.M+5全部都是段的属性,M+6中,也有段属性的存在.

M+6,和M+5展开查看.

其中 BLT3,BLT2,BLT1,BLT0这4位,是段界限的4位.剩余的是段属性

4.2段属性介绍.

根据上面的表展开得知,段属性分别有

G D 0 AVL  P DPL DT1  TYPE

那么分别是什么意思?

G : 历史位, 当G = 1的是否,段界限需要 * 4K(0-4095),G = 0的是否,那么就是按照8位来计算,也就是一个字节.

D: D位方向位.我们的堆栈保护内存的时候,堆栈和我们正常的数据结构是相反的.段界限我们应该+那么堆栈就需要减,所以需要个方向位调整

BLT5, 保留位

AVL: 应用软件可使用位.

P: 这个段这个内存是否有效

DPL: 特权位,判断你是0环还是3环

DT位: 描述我们这个表的类型是用户还是系统.DT = 1描述系统. 

TYPE:位,这个则是表明了内存属性是可读,可写,还是可执行.

TYPE位展开.

如果想要一块内存可读可执行,可写.那么需要建立两个表.分别让TYPE为 = 2,然后 = 8即可.

因为段可以重叠.所以可以这样操作,所以就有了修改内存保护属性的API,inter官方承认的.

五丶什么是描述符,以及全局描述符表,局部描述符表.

描述符:

  也就是我们说的三个字段.我们这三个字段合在一起成为了一张表.这个表则成为存储段描述符. 存储段信息的表.

局部描述符表:

   我们知道针对我们一个进程我们可以建立多个存储段描述符表.来保护我们的内存,那么CPU访问的是否则会进行校验.

那么这个则是局部描述符表.简称 LDT表.

  那么如果多个进程我们就需要多个描述符表.各自放在自己的低2G空间,高2G空间的描述符表是一样.

那么此时就产生了冗余问题.高2G的描述符表都是一样的,所以建立一个全局的描述符表. 简称GDT

LDT表只需要操作系统给表的地址即可.这些都是操作系统在实模式启动的是否进行填写的.

其中LDT = A进程,那么就执行A进程的操作.切换进程,并且保存进程的各种信息.以及各种表. 如果我们换成了B进程,那么就会切换到B进程.

如果我们手动切换,则是进行内核操作了,也就是所说的ROOTKIT,(内核补丁)的技术了.(我猜大部分学习内核的都是学习这种)

六丶段选择子,进行查表.

什么是段选择子.段选择子是翻译的.我们在32为的段选择子是我们的段寄存器. 这些段寄存器都用来保护内存了.

段当下表进行查表动作.其中后2为是RPL,也是特权指令. 如果查表的是否RPL和描述符表的DPL一至则可以进行查表.不会出错.  BLT2是表示查询那个表, TL = 1表示是LDT表,TL = 0 表示GDT表.

3环下有读取GDT和LDT描述符表的指令

SGDT [内存]  读取SGDT表的起始位置到你指定的内存.  3环下可以读不可以写.  LGDT[内存] 写,ring3不能执行.

其中如果在ring3下执行SGDT那么操作系统给你的是一个错误的值.

学习内核知识建议读书 + 视频. 资料只能简单看看

课堂代码资料:   链接:https://pan.baidu.com/s/1qZIt6hu 密码:quao

转载请注明出处: http://www.cnblogs.com/iBinary/

原文地址:https://www.cnblogs.com/iBinary/p/8253735.html