逻辑地址--------------》线性地址------------》 物理地址

      分段          分页

GDT是[gobal (segment) descriptor table]的缩写,它保存所有segment的信息

内存管理时,不让多进程的程序出现内存冲突的一解决方案是Segmentation。4GB的内存可以任何分割,每块的初始地址都是0。另外还有一种复杂的内存管理方案,既Paging,目前主流的操作系统都是采用这种方式。本文的OS为了实现简单,只采用Segmentation方案。

我们规定1个Segmentation的信息有:

l         Size

l         初始地址

l         属性(读写权限等)。

每个Segementation信息占8Byte,既64bit。我们把所有的Segment编号,并把编号保存在CPU内的16位segment register里。Segment register可以处理0到8191个segment,本来16位的register应该可以处理0到65535个segment,但是因为register的低3为不能使用,所以最多只到8191。为了保存8192个segment(每个8bytes),需要64KB的容量,CPU是无法保存这么多数据的,因此需要内存的帮助。GDT是[gobal (segment) descriptor table]的缩写,它保存所有segment的信息。Segment的初始地址和有效设定个数保存在CPU的GDTR寄存器内。另外一个IDT是[interrupt descriptor table]的缩写,用来处理一些内部或外部的中断。例如键盘,鼠标,软驱,硬盘,CDROM,网卡等。IDT可以处理0到255编号的中断,例如当123号中断发生时,调用XXX函数。IDT的信息和GDT相似,也是每个8bytes。本系统的GDT分配在内存的0x27000到0x27ffff。IDT是0x26f800到0x26ffff,占2KB。

我们将Segment定义为:

struct SEGMENT_DESCRIPTOR {

short limit_low, base_low;

char base_mid, access_right;

char limit_high, base_high;

};

Segment信息的初始地址由32bit组成,成为Base地址,分为3个部分,low(2byte),mid(1byte),high(1byte),如此分割是为了兼容80286 CPU。

Segment信息的大小成为Limit,Limit最大可以为4GB,当然是在32位机。这样就需要4Byte,和Base地址一共需要8个byte,但这样就没有保存属性的位置。为了留给属性空间,只能给Limit 20bit,最大能表示1MB,当然这样是不能满足目前主流的32bit机的。Intel为了解决这个问题,规定了在属性里留有1位称为G bit(G表示granularity,粒度),limit的单位不是Byte,而是Page,PC的CPU的1个Page表示4KB。这样4KB*1MB=4GB。为了能表示20bit的Limit,我们使用2个Byte。其中多出的4bit用来表示属性。

Segment的属性由Limit多出的4bit加上剩下的8个bit表示,也称访问权限。访问权限的构成:

XXXX0000XXXXXXXX(X代表0或1)

上4bit是386以后使用的扩张访问属性,由[GD00]构成,G表示上面提到的粒度,D表示Segment mode,1表示32位机,0表示16位机。下8bit继承80286时代的Segement属性。8bit的属性内容非常多,在这里,我们常用的有如下几个:

00000000(0x00):未使用的Descriptor table

10010010(0x92):系统专用,可读写,不能执行

10011010(0x9a):系统专用,可执行,可读,不可写

11110010(0xf2):应用程序使用,可读写,不可执行。

11111010(0xfa):应用程序可用,可执行,可读,不可写。

属性规定了系统和应用程序使用的读写执行权限。系统模式称为Ring0,应用程序模式称为Ring3,中间的Ring1和Ring2模式是系统服务。Ring0管理Ring3,例如,Ring3的应用程序在请求Load LGDT时,操作系统将否决该请求,以保证系统的安全性。下面是新增加的源代码文件。

http://www.kissthink.com/archive/An-operating-system-of-personal-development-and-IDT-GDT-Initialization.html

1. What is GDT
在Protected Mode下,一个重要的必不可少的数据结构就是GDT(Global Descriptor Table)。
为什么要有GDT?我们首先考虑一下在Real Mode下的编程模型:
在Real Mode下,我们对一个内存地址的访问是通过Segment:Offset的方式来进行的,其中Segment是一个段的Base Address,一个Segment的最大长度是64 KB,这是16-bit系统所能表示的最大长度。而Offset则是相对于此Segment Base Address的偏移量。Base Address+Offset就是一个内存绝对地址。由此,我们可以看出,一个段具备两个因素:Base Address和Limit(段的最大长度),而对一个内存地址的访问,则是需要指出:使用哪个段?以及相对于这个段Base Address的Offset,这个Offset应该小于此段的Limit。当然对于16-bit系统,Limit不要指定,默认为最大长度64KB,而 16-bit的Offset也永远不可能大于此Limit。我们在实际编程的时候,使用16-bit段寄存器CS(Code Segment),DS(Data Segment),SS(Stack Segment)来指定Segment,CPU将段积存器中的数值向左偏移4-bit,放到20-bit的地址线上就成为20-bit的Base Address。
到了Protected Mode,内存的管理模式分为两种,段模式和页模式,其中页模式也是基于段模式的。也就是说,Protected Mode的内存管理模式事实上是:纯段模式和段页式。进一步说,段模式是必不可少的,而页模式则是可选的——如果使用页模式,则是段页式;否则这是纯段模式。
既然是这样,我们就先不去考虑页模式。对于段模式来讲,访问一个内存地址仍然使用Segment:Offset的方式,这是很自然的。由于 Protected Mode运行在32-bit系统上,那么Segment的两个因素:Base Address和Limit也都是32位的。IA-32允许将一个段的Base Address设为32-bit所能表示的任何值(Limit则可以被设为32-bit所能表示的,以2^12为倍数的任何指),而不象Real Mode下,一个段的Base Address只能是16的倍数(因为其低4-bit是通过左移运算得来的,只能为0,从而达到使用16-bit段寄存器表示20-bit Base Address的目的),而一个段的Limit只能为固定值64 KB。另外,Protected Mode,顾名思义,又为段模式提供了保护机制,也就说一个段的描述符需要规定对自身的访问权限(Access)。所以,在Protected Mode下,对一个段的描述则包括3方面因素:[Base Address, Limit, Access],它们加在一起被放在一个64-bit长的数据结构中,被称为段描述符。这种情况下,如果我们直接通过一个64-bit段描述符来引用一个段的时候,就必须使用一个64-bit长的段积存器装入这个段描述符。但Intel为了保持向后兼容,将段积存器仍然规定为16-bit(尽管每个段积存器事实上有一个64-bit长的不可见部分,但对于程序员来说,段积存器就是16-bit的),那么很明显,我们无法通过16-bit长度的段积存器来直接引用64-bit的段描述符。
怎么办?解决的方法就是把这些长度为64-bit的段描述符放入一个数组中,而将段寄存器中的值作为下标索引来间接引用(事实上,是将段寄存器中的高13 -bit的内容作为索引)。这个全局的数组就是GDT。事实上,在GDT中存放的不仅仅是段描述符,还有其它描述符,它们都是64-bit长,我们随后再讨论。
GDT可以被放在内存的任何位置,那么当程序员通过段寄存器来引用一个段描述符时,CPU必须知道GDT的入口,也就是基地址放在哪里,所以Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此积存器,从此以后,CPU就根据此积存器中的内容作为GDT的入口来访问GDT了。
GDT是Protected Mode所必须的数据结构,也是唯一的——不应该,也不可能有多个。另外,正象它的名字(Global Descriptor Table)所揭示的,它是全局可见的,对任何一个任务而言都是这样。
除了GDT之外,IA-32还允许程序员构建与GDT类似的数据结构,它们被称作LDT(Local Descriptor Table),但与GDT不同的是,LDT在系统中可以存在多个,并且从LDT的名字可以得知,LDT不是全局可见的,它们只对引用它们的任务可见,每个任务最多可以拥有一个LDT。另外,每一个LDT自身作为一个段存在,它们的段描述符被放在GDT中。
IA-32为LDT的入口地址也提供了一个寄存器LDTR,因为在任何时刻只能有一个任务在运行,所以LDT寄存器全局也只需要有一个。如果一个任务拥有自身的LDT,那么当它需要引用自身的LDT时,它需要通过LLDT将其LDT的段描述符装入此寄存器。LLDT指令与LGDT指令不同的时,LGDT指令的操作数是一个32-bit的内存地址,这个内存地址处存放的是一个32-bit GDT的入口地址,以及16-bit的GDT Limit。而LLDT指令的操作数是一个16-bit的选择子,这个选择子主要内容是:被装入的LDT的段描述符在GDT中的索引值——这一点和刚才所讨论的通过段积存器引用段的模式是一样的。

《自己动手写操作系统》是本很有意思的书。不过于渊前辈对某些内容没有给出更多的解释,让我们这些新米很难上手。 书中第3章介绍保护模式的时候,题到了GDT这个东东。在这里给出一个更浅显的介绍。 在介绍GDT这个概念前,现介绍一下segmentation这个概念。 大家还记得ORG这个汇编命令吧。书的开篇,给出的boot.asm代码的第一行就是:     ORG 0x7c00 这个ORG就是告诉CPU,程序被载入内存的0x7c00这个地方了。也就是说CPU从这个内存地址开始读取指令就OK了。 但是,现在的CPU同时执行好几个程序是很正常的事情。那么不同的程序使用的内存就有可能重叠,这样载入程序的话就会发生冲突。解决方法之一就是把把内存分成若干个段(segmentation)。每段放一个程序,这样每个程序都可以声明ORG 0了。很方便。 (另一个解决方法是paging,这个以后有机会在谈。) 为了能使用segmentation, 每个段(segment)都需要清楚的表明以下信息: - 段(segment)的大小。 - 段在内存中的开始位置。 - 段的管理属性。比如只读,系统专用等等 CPU用8bytes(=64bits)来表示这些信息(叫做descriptor)。但是,CPU用来指定段的register只有16bits。就算是32bit的CPU,这个register也是16bits的。那怎么办呢? 方法就是每个segment给一个"编号"(正式叫做segment selector),然后在把这个编号放在register里就好了。剩下的工作就是让编号和segment一一对应起来。 大概的感觉是这样的: 1号segment的大小是4GB,从内存的0000开始。(也就是指整个内存了。) 2号segment的大小是xxKB,从内存的xxxx开始。。。。 segment的编号为0~8191。 刚才说了,segment register是16bits,按理说应该可以存放2**16 = 65536个编号。但是由于CPU设计式样的问题,最低3bits不能用,也就是只有13bits可用,刚好是2**13=8192个。 每个segment的描述需要8bytes,一共就需要8192*8=64KB。CPU里可没有这么大的空间。所以就放在内存里好了。这个64KB(可以小于这个值),记录了分段信息的数据结构就叫做GDT -- Global (segment) Descriptor Table. 我不知道这个东东的正式翻译是什么,直译过来就是一个存放了描述分段信息的表。 这次介绍了GDT的概念和这个东东是怎么来的,干什么用的。 下次介绍segment descriptor的数据结构,和如何使用

为什么要有GDT的更多相关文章

  1. 操作系统篇-分段机制与GDT|LDT

    || 版权声明:本文为博主原创文章,未经博主允许不得转载. 一.前言     在<操作系统篇-浅谈实模式与保护模式>中提到了两种模式,我们说在操作系统中,其实大部分时间是待在保护模式中的. ...

  2. Eclipse下使用GDT插件无法登陆GAE & GDT无法上传JAVA代码

    今天更新github主页的过程中,想使用GAE部署一个Java Web服务来更好的支持网站动态性(关键是利用了免费的GAE资源),结果遇到了2个大问题. 1.GDT插件无法登陆GAE账户 错误1:登陆 ...

  3. GDT 学习笔记逻辑地址和线性地址计算,因为是自学,所以这只是我的个人理解,不对的请大家指导。

    在 bochs 刚开始的时候 gdt 是未知的,需要通过实模式的16位代码段初始化 gdt 信息, 在 lgdt 指令之后,即可以使用程序自定义的 GDT 表了. 假如:gdt 初始地址为 0x7c7 ...

  4. GDT,LDT,GDTR,LDTR 详解,包你理解透彻(转)

    引自:http://www.techbulo.com/708.html 一.引入 保护模式下的段寄存器 由 16位的选择器 与 64位的段描述符寄存器 构成 段描述符寄存器: 存储段描述符 选择器:存 ...

  5. [自制简单操作系统] 2、鼠标及键盘中断处理事件[PIC\GDT\IDT\FIFO]

    1.大致介绍: >_<" 大致执行顺序是:ipl10.nas->asmhead.nas->bootpack.c PS: 这里bootpack.c要调用graphic. ...

  6. EPROCESS 进程/线程优先级 句柄表 GDT LDT 页表 《寒江独钓》内核学习笔记(2)

    在学习笔记(1)中,我们学习了IRP的数据结构的相关知识,接下来我们继续来学习内核中很重要的另一批数据结构: EPROCESS/KPROCESS/PEB.把它们放到一起是因为这三个数据结构及其外延和w ...

  7. 非常好!!!【从头开始写操作系统系列】实现一个-GDT(1)【转】

    转自:http://blog.csdn.net/luoyhang003/article/details/47338019 权声明:本文为博主原创文章,未经博主允许不得转载.(文章来源:http://b ...

  8. 段描述符表(GDT+LDT)的有感

    [0]写在前面 要知道,在汇编中,代码的装入顺序决定了在内存中的地址位置.所有的代码或者数据都在硬盘上,当调试或者启动的时候,加载到内存:当需要对数据进行处理的时候,我们通过将数据从内存载入到regi ...

  9. Windows xp下IDT Hook和GDT的学习

    一.前言   对于IDT第一次的认知是int 2e ,在系统调用的时候原来R3进入R0的方式就是通过int 2e自陷进入内核,然后进入KiSystemService函数,在根据系统服务调用号调用系统服 ...

  10. (转)GDT与LDT

    网址:http://blog.csdn.net/billpig/article/details/5833980 保护模式下的段寄存器 由 16位的选择器 与 64位的段描述符寄存器 构成段描述符寄存器 ...

随机推荐

  1. Asp.net处理程序(第六篇)

    四.Web服务处理程序 对于Web服务来说,标准的方式是使用SOAP协议,在SOAP中,请求和回应的数据通过XML格式进行描述.在Asp.net 4.0下,对于Web服务来说,还可以选择支持Ajax访 ...

  2. delphi win64 DEBUG不能进预设断点的问题

    delphi win64 DEBUG不能进预设断点的问题  delphi win64,debug模式下运行,如果含有中文路径,不能进断点,音频跟踪.而同样的代码,DELPHI WIN32却没有这个问题 ...

  3. Extjs Ajax 分页

    var storeCpye = new Ext.data.GroupingStore({ proxy : new Ext.data.HttpProxy({ url : 'cxgl_cpye.app?d ...

  4. 如何中断正在执行IO的 Quartz 作业

    Interrupt a Quartz job that doing IO 如果你想中断正在执行IO的 Quartz 作业,在你使用 InterruptibleChannel 时这是可行的.引用一下Or ...

  5. Python kmean

    # -*- coding: utf-8 -*-from sklearn.cluster import KMeansfrom sklearn.externals import joblibimport ...

  6. [Linux] Systemd 入门教程:命令篇

    reference : http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html Systemd 是 Linux 系统 ...

  7. 让Mac风扇面对PD不再疯狂

    对于所有喜欢Mac操作系统的用户来说,如果办公环境必须使用Windows及Windows程序,那一定会非常崩溃,因为你很可能使用了Parallels Desktop来运行你的Windows虚拟机,那么 ...

  8. Gedit

    Use Gedit as Remote File Editor via FTP and SSH Migrating from Windows Vista to Ubuntu 8.04 and have ...

  9. Visual GC(监控垃圾回收器)

    Java VisualVM默认没有安装Visual GC插件,需要手动安装,JDK的安装目录的bin目露下双击jvisualvm.exe,即可打开Java VisualVM,点击菜单栏 工具-> ...

  10. otl翻译(11) -- OTL的迭代器

    OTL stream read iterator 这个类是一个像传统的JDBC中的getter()操作一样扩展了OTL流的模板类.它现在还不支持UNICODE字符集.它对otl_refcur_stre ...