CPU实模式和保护模式、全局描述符表GDT、Linux内核中GDT和IDT的结构定义_linuxgdt-程序员宅基地

技术标签: 汇编语言  内核结构定义  GDT  操作系统研发和研究  保护模式  GDT_ENTRY_INIT  段描述符  

一 计算机实模式和保护模式

 

实模式
在实模式下,内存被限制为仅有1M字节(220 字节)。有效的地址从00000到FFFFF (十六进制)。
这些地址需要用20位的数来表示。一个20位的数不适合任何一个8086的16位寄存器。
Intel通过利用两个16位数值来决定一个地址的方法来解决这个问题。开始的16位值称为段地址(selector)。
段地址的值必须存储在段寄存器中。第二个16位值称为偏移地址(offset)。

16位保护模式
在实模式下,一个段地址的值是物理内存里的一节的首地址。在保护模式下,一个段地址的值是一个指向描述符表的指针
。两种模式下,程序都是被分成段。在实模式下,这些段在物理内存的固定位置而且段地址的值表示段开始处所在节的首
地址。在保护模式下,这些段不是在物理内存的固定的地址。事实上,它们不一定需要在内存中。

保护模式使用了一种叫做虚拟内存的技术。虚拟内存的基本思想是仅仅保存程序现在正在使用的代码和数据到内存中。其
它数据和代码暂时储存在硬盘中直到它们再次需要时。

在保护模式下,每一段都分配了一条描述符表里的条目。这个条目拥有系统想知道的关于这段的所有信息。这些信息包括
:现在是否在内存中;如果在内存中,在哪;访问权限(例如: 只读)。段的条目的指针是储存在段寄存器里的段地址值

32位保护模式
80386引入了32位保护模式。
386 32位保护模式和286 16位保护模式之间最主要的区别是:
    1 偏移地址扩展成了32位。这就允许偏移地址范围升至4G。因此,段的大小也升至4G。
    2 段可以分成较小的4K大小的单元,称为内存页。虚拟内存系统工作在页的方式下,代替了段方式。这就意味着一段
在任何一个时刻只有部分可能在内存中。在28616位保护模式下,要么整个段在内存中,要么整个不在。 

    在Windows 3.x系统中,标准模式为286 16位保护模式而增强模式为32位保护模式。Windows 9X,Windows
NT/2000/XP,OS/2和Linux都运行在分页管理的32位保护模式下。

 

二 全局描述符表GDT

 

    GDT,Global Descriptor Table。
    IA32允许将一个段的基地址设置为32bit所能表示的任何值,limit(段大小)则可以设置成以2^12为倍数的任何值。
    在保护模式下,对一个段的描述包括以下三个方面:[Base Address,Limit,Access],他们加在一起被放在一个64bit
长的数据结构中,被成为段描述符。
    在这种情况下,如果我们直接通过一个64bit段描述符来引用一个段的时候,就必须使用一个64bit长的段寄存器装入
这个段描述符。
    把这些长度为64bit的段描述符放入一个数组中,而将段寄存器中的值作为下标索引来间接引用(事实上就是将段寄
存器中的高13bit的内容作为索引)。这个全局数据就是GDT。

 

三 Linux中GDT和IDT的结构定义

 

GDT,IDT都是全局的。LDT是局部的(在GDT中有它的描述符);

GDT用来存储描述符(门或非门);系统中几个CPU,就有几个GDT

struct gdt_page {

    struct desc_struct gdt[GDT_ENTRIES];

} __attribute__((aligned(PAGE_SIZE)));

DECLARE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page);


IDT整个系统只有一个;
系统启动时候需要初始化GDT和IDT;


描述符结构定义,在<arch/x86/include/asm/desc_defs.h>

struct desc_struct {

    union {

        struct {

            unsigned int a;

            unsigned int b;

        };

        struct {

            u16 limit0;

            u16 base0;

            unsigned base1: 8, type: 4, s: 1, dpl: 2, p: 1;

            unsigned limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8;

        };

    }; 
} __attribute__((packed));

上面第一个匿名结构体用来作为成员访问取值的出口,下面第二个结构体对真实的成员设置值的入口。

字段:

limit:段长度

base:段的首字节的线性地址,有base0,base1,base2三部分构成

type:段的类型和存取权限

s:系统标志。1-系统段;0-普通段

dpl:描述符特权级

p:segment-Present。linux下总是1

avl:linux不用

d:区分代码段还是数据段

g:段大小粒度。以4K倍数计算

 

在32位机器上,这就是所有描述符的数据结构;

typedef struct desc_struct gate_desc;

typedef struct desc_struct ldt_desc;

typedef struct desc_struct tss_desc;

 

由于三类描述符都是一个结构类型,从而一律使用下面宏初始化在GDT中表项

#define GDT_ENTRY_INIT(flags, base, limit) { { { \

        .a = ((limit) & 0xffff) | (((base) & 0xffff) << 16), \

        .b = (((base) & 0xff0000) >> 16) | (((flags) & 0xf0ff) << 8) | \

            ((limit) & 0xf0000) | ((base) & 0xff000000), \

    } } }

 

但是在64位机器上,Linux则进行了细致划分;
无论是32位还是64位机器上,都使用typedef重新定义,以提供给系统其他使用此描述符的部分一致的类型名;

 

区分描述符的枚举量

enum {

    GATE_INTERRUPT = 0xE,

    GATE_TRAP = 0xF,

    GATE_CALL = 0xC,

    GATE_TASK = 0x5,

};


enum {

    DESC_TSS = 0x9,

    DESC_LDT = 0x2,

    DESCTYPE_S = 0x10,  /* !system */

};

 

系统GDT,IDT指针描述结构

struct desc_ptr {

    unsigned short size;

    unsigned long address;

} __attribute__((packed)) ;


这个结构记录了系统的GDT或者IDT的大小以及在系统中的线性基址;

Reference:

    <arch/x86/include/asm/desc_defs.h>

 

四 如何通过段描述符访问内存

 

当我们要访问某个段中的一个地址时候:
    1 从GDTR中拿到GDT在内存中的基地址,得到段描述符表; 

    2 从段选择子中的前13位得到我们要访问的段的描述符在段描述符表中的索引; 

    3 从段描述符表中得到要访问的段的描述符,得到其基地址;
    4 基地址加上偏移地址就是我们要访问的内存地址(这里是虚拟地址,接下来是分页机制的功能将虚地址转换为物理
地址,)

 

看内核相关源码涉及到 AT&T汇编,和Intel汇编语法不同;先学部分AT&T汇编语法;

    操作数排列是从源(左)到目的(右),如"movl %eax(源), %ebx(目的)";

    符号常数直接引用 如:
value: .long 0x12a3f2de
movl value , %ebx

    会见到如下的指令:push,pushl,pushfl;

    操作数的长度用加在指令后的符号表示b(byte, 8-bit), w(word, 16-bits), l(long, 32-bits);
    如
        "movb %al, %bl",
        "movw %ax, %bx",
        "movl %eax, %ebx",
    目前还不知道pushfl是啥;

    如果没有指定操作数长度的话,编译器将按照目标操作数的长度来设置。

参阅
https://blog.csdn.net/cwcmcw/article/details/21640363
http://blog.chinaunix.net/uid-29113598-id-5210949.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/bcbobo21cn/article/details/106126764

智能推荐

计算机二叉树节点计算公式,二叉树节点数该怎么计算?有几种算法?-程序员宅基地

文章浏览阅读1.6w次,点赞7次,收藏13次。每一棵二叉树中都有左右两棵子树,子树中又有无数节点,那你们知道子树中的节点该怎么计算吗?快来跟小编了解一下吧。二叉树算法概念对于任何一棵二叉树来说,其叶子结点的数目为n0,且其度数为2的结点数n2,则n0=n2+1.证明:对于此二叉树:设其度数为1的结点数为n1. 从下往上看,每个结点都会有一个边朝上,除了根结点,则边总数为:N=n0+n1+n2-1 ①从上往下看,度数为2的结点有两个边,度数为1..._二叉树节点数计算

deepstream-test5-程序员宅基地

文章浏览阅读908次。英文说明1、可以与服务器联通,接发消息2、物联网 协议 supported by nvmsgbroker plugin is listed in the DeepStream Plugin guide_deepstream-test5

图解KMP算法(研究总结next(数组)算法实现)_kmp算法,next函数实验总结-程序员宅基地

文章浏览阅读828次。记录,防止忘记!!!一 从现象入手Kmp算法 原始匹配方法: Kmp匹配算法(从三张图了解)总结发现:当pattern中第 j 处位置(j > 0)配不上时,让pattern中第m号元素与主串当前位置比较,m = pattern串中 j-1的next(即next[j - 1])+ 1.前缀:至少不含最后一个元素后缀:至少不含第一个元素于是有个大胆的想法:将上述图中这些蓝色字体封装成一个next数组(也叫match数组)..._kmp算法,next函数实验总结

WPS表格 JSA 学习笔记_wps jsa-程序员宅基地

文章浏览阅读1w次,点赞24次,收藏136次。WPS表格 JSA 学习笔记自定义公式单元格内文字颜色Sheet 工作表工作表引用Range单元格引用填写公式单元格赋值Cells 遍历单元格Sheet 范围内遍历Range 范围内遍历参考资料自定义公式function 加括号(区域, 左括号, 右括号){ return `${左括号}${区域}${右括号||左括号}`;}单元格内文字颜色function test(){ var len = Range("A1").Value2.length; for(let i = 1; i<=_wps jsa

Python 打印杨辉三角形(包括金字塔的款式的~~)_python杨辉三角金字塔-程序员宅基地

文章浏览阅读4.7w次,点赞41次,收藏136次。今天是愉快的星期天,终于可以睡个懒觉了。今天是准备被这周的所以内容归纳一下的,然后看到的经典的杨辉三角形,所以就把代码整理了一下。给他取了个狂野的名字。==================================疯狂的杨辉三角形===================================1.普通的杨辉三角形​​ 杨辉三角形的规则就是出意每行的第一个数字之外,..._python杨辉三角金字塔

IDEA配置jdk-程序员宅基地

文章浏览阅读10w+次,点赞38次,收藏80次。提前安装jdk,配置环境变量一、配置jdk1、依次点开File -->Project Structure,点击左侧标签页,点击SDKs2、点击+号,选SDK3、在弹出框选择jdk路径(我本地装了两个jdk,大家可以忽略)4、点击OK,可以看到jdk配置好了5、按照这个顺序,可以配置多个jdk_idea配置jdk

随便推点

SuperGO外设下载地址安装,穿透模式使用-程序员宅基地

文章浏览阅读1.5w次。SuperGo下载地址安装使用教程这里给大家分五个步骤进行教学第一步:打开MM/纵行地图/SuperGo软件的下载网站 www.tngps.com(用Safari浏览器进行操作),在APPstore下载并安装“TestFlight”软件,安装以 后就不用管这个软件了第二步:下载完“TestFlight”后,返回刚才的网站点击“开始测试”,下载MM软件并安装。第三步:打开MM/纵行地图/SuperGo软件后插入设备,等待数秒等待软件连接成功后进入操作界面。第四步:在“搜索.._supergo

iOS开发之旅--_isa和_imp指针-程序员宅基地

文章浏览阅读358次。2019独角兽企业重金招聘Python工程师标准>>> ..._ios 怎么打印 imp

获取url参数的方法-程序员宅基地

文章浏览阅读2.1k次。获取url参数主要有两种方法第一种是利用字符串的分割方法,将url以“&”和“=”做分割,得到参数数组,然后再利用数组的迭代方法中的filter()方法,筛选并返回我们需要的数据。function GetQueryString(name) { //获取url中“?”后边的部分,并将结果以“&”分割成数组 var paras = url.split('?')[1].spli_获取url参数的方法

Linux系统优化(Centos7.x系统)_sed -i 's/histsize\=1000/histsize\=0/g' /etc/profi-程序员宅基地

文章浏览阅读824次,点赞2次,收藏4次。Linux系统优化1、更新yum官方源2、调整TCP/IP网络参数3、修改 SHELL 命令的 history 记录个数4、定时校正服务器时间5、关闭IPV6网络6、修改文件描述符限制7、配置网卡自启8、关闭文件记录时间增加磁盘IO9、优化ssh登陆总结优化脚本1、更新yum官方源Centos里面默认的官方源是很慢的,为了让安装软件更快,我i这里选择把源更换为阿里云的源(Centos 7.n)1)下载repo文件wget -P /etc/yum.repos.d/ http://mirrors.al_sed -i 's/histsize\=1000/histsize\=0/g' /etc/profile

Leetcode之括号生成(C++)_c++括号逐步生成结果-程序员宅基地

文章浏览阅读380次。今天,我们来看一下LeetCode的第22道题:括号生成题目描述给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。例如,给出 n = 3,生成结果为:[ "((()))", "(()())", "(())()", "()(())", "()()()"]思路:首先,我们要明确题目的要求,给出n对括号,生成这n对括号的所有..._c++括号逐步生成结果

浙大首届人工智能本科生9月入学,纳入竺院图灵班-程序员宅基地

文章浏览阅读1k次。作者:澎湃新闻见习记者 张唯 实习生 邹静祺据澎湃新闻(www.thepaper.cn)报道,刚刚获批的浙江大学人工智能本科专业将于2019年开始招生,学生纳入新设立的竺..._浙大图灵班课程设置

推荐文章

热门文章

相关标签