zoukankan      html  css  js  c++  java
  • 内核第二讲,内存保护的实现,以及知识简介,局部描述符,全局描述符.

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

    一丶了解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/

  • 相关阅读:
    基本二叉搜索树的第K小元素
    sklearn常见分类器(二分类模板)
    python图论包networks(最短路,最小生成树带包)
    PAT 甲级 1030 Travel Plan (30 分)(dijstra,较简单,但要注意是从0到n-1)
    PAT 甲级 1029 Median (25 分)(思维题,找两个队列的中位数,没想到)*
    Oracle 10g ORA-12154: TNS: could not resolve the connect identifier specified 问题解决! 我同事遇到的问题。 username/
    JavaScritpt的DOM初探之Node(一)
    怎样实现动态加入布局文件(避免 The specified child already has a parent的问题)
    Ubuntu 14.04下单节点Ceph安装(by quqi99)
    卡片游戏
  • 原文地址:https://www.cnblogs.com/iBinary/p/8253735.html
Copyright © 2011-2022 走看看