在操作系统中,全局描述符是什么?GDT又是什么?在进入保护模式之前,准备好GDT和GDT中的描述符是必须的吗?用汇编代码怎么创建描述符?本文解答上面几个问题。

在实模式下,CPU是16位的,意思是,寄存器是16位的,数组总线(data bus)是16位的,但地址总线是20位的。物理内存地址的计算公式是:

$$

物理地址 = 段地址 * 16 + 偏移量

$$

段地址和偏移量都是16位的,能寻址的最大内存地址是1M。

1M是怎么计算出来的?2的20次方就是1M,能表示的内存地址是 0~(2的20次方-1)。用简单例子来理解,1位十进制数能表示的最大数是10 - 1 = 9,但1位十进制数能表示的数却是 0 ,和 1-9 ,总计10个数字。

若一个内存地址是20:30,最终内存地址是:20 * 16 + 30

在保护模式下,内存地址仍然用“段地址:偏移量”的方式来表示。不过,“段地址”的含义不同于实模式下的“段地址”。在实模式下,段地址是物理地址的高地址部分,具体说,是高16位部分。而在保护模式下,段地址是选择子,指向一个结构,这个结构描述了一个内存区域,告知该区域的内存地址从哪里开始,在哪里结束,还告知了这片内存能不能被访问、能不能被读取等数据。这个结构组成一个集合,叫GDT,而这个结构叫GDT项,它有一个术语,叫“描述符”。

GDT的作用是提供段式存储机制。段式存储机制由段寄存器和GDT共同提供。段寄存器提供段值,即描述符在GDT中的索引,也就是选择子。根据选择子在GDT中找到目标描述符。这个描述符中包含一段内存的初始地址、这段内存的最大地址、这段内存的属性。

GDT的构成

GDT项即全局描述符的长度是8个字节,64个bit,64个位,063位,而不是164位。下图是写了位编号的8个字节。真实的全局描述符是不折行的,这里无法在一行显示全部数据,因此折行了。

63|62|61|60|59|58|57|56| 55|54|53|52|51|50|49|48| 47|46|45|44|43|42|41|40| 39|38|37|36|35|34|33|32| 31|30|29|28|27|26|25|24| 23|22|21|20|19|18|17|16| 15|14|13|12|11|10|09|08| 07|06|05|04|03|02|01|00|

15|14|13|12|11|10|09|08| 07|06|05|04|03|02|01|00|。段界限1。段界限的 0~15 位。描述符的 0~15 位。

39|38|37|36|35|34|33|32| 31|30|29|28|27|26|25|24| 23|22|21|20|19|18|17|16|。段基址1。段基址的 0~23位。描述符的 16~39位。

55|54|53|52|51|50|49|48| 47|46|45|44|43|42|41|40|。很复杂,很碎片化,需进一步放大观察。

43|42|41|40|,TYPE。4位。

44,S。是否为系统段(待验证)。1位。

46|45,DPL。2位。

47,P。1位。

上面是一个字节,下面是第二个字节。

51|50|49|48|。段界限2。段界限的第 16~19 位。描述符的第 48~51 位。段界限一共有20位。

52。AVL。1位。

53。0。1位。

54。D/B。1位。

55。G。1位。

段属性占用空间的位数:4 + 1 + 2 + 1 + 1 + 1 * 3 = 12。

63|62|61|60|59|58|57|56|。段基址2。段基址的第 24~31 位。描述符的第 56~63 位。段基址一共有32位。

描述符的结构比较复杂,要记住它,有点困难,不过并非不可能记住。作者觉得没有必要一个字节不差地背诵出来。

选择子

描述符的选择子的长度是16位。

15|14|13|12|11|10|09|08| 07|06|05|04|03|02|01|00|

01|00|,RPL。

02,T1。

15|14|13|12|11|10|09|08| 07|06|05|04|03,描述符在GDT中的索引。

段式存储机制的寻址方式

段地址存储的是描述符的选择子,根据选择子能找到GDT中对应的描述符。从描述符中获取段基址,然后加上段式存储机制中的偏移量,就是线性地址。在当前语境下,线性地址等同物理地址。

概念比较

逻辑地址。段式机制的地址,例如“段地址:偏移量”,就是逻辑地址。

线性地址。在保护模式下,用逻辑地址中的段地址从GDT中找到描述符,然后从描述符中获取段的基址,段基址加上偏移量的结果就是线性地址。

如上文所言,线性地址目前可视为物理地址。开启分页机制后,线性地址不能等同于物理地址。物理地址是物理内存的一个编号。

作者的疑问

进入保护模式前,为什么需要创建好描述符、选择子、GDT?这些是必要条件吗?

作者曾认为这些不是必须。再次了解段式存储机制后,改变了看法:进入保护模式前,必须准备好GDT、描述符和描述符选择子。这是由保护模式下的内存寻址方式决定的。

无论是在实模式下还是保护模式下,都需要使用内存。在保护模式下,怎么找到某片内存呢?保护模式下,使用段式机制。回忆一下,段式存储机制的寻址方式是:

$$

段地址(选择子)-----》在GDT中找到描述符----》在描述符中找到段基址----》段基址+偏移量 = 线性地址

$$

不在进入保护模式前准备好选择子、GDT、描述符,就无法在保护模式中使用内存。

作者还有一个疑问:上面的寻址过程是CPU自动完成的吗?

实现描述符

C语言
描述符

下面内容的前提是,32位CPU。

struct {
int segmentLimit1:16; // 段界限1
int segmentBaseAddress1:24; // 段基址1
char attributeType:4; // 段属性,TYPE
char attributeS:1; // 段属性,S
char attributeDPL:2; // 段属性,DPL
char attributeP:1; // 段属性,P
char segmentLimit2:4; // 段界限2
char attributeAVL:1; // 段属性,AVL
char attributeZero:1; // 段属性,值为0
char attributeDB:1; // 段属性,DB
char attributeG:1; // 段属性,G
char segmentBaseAddress2; // 段基址2
}GlobalDescriptor;

上面的用法是错误的。对位域的使用是错的,换成int来使用位域也无能力写正确,因为太麻烦。在这个知识点耗费了不少时间。

参考书中代码后,写出下面的代码:

struct{
unsigned short segmentLimitLow; // 段界限1,16位,0~15 位。描述符的第 0~15 位。
unsigned short segmentBaseAddressLow; // 段基址低16位,0~15 位。描述符的第 16~31 位。
unsigned char segmentBaseAddressMid; // 段基址 16~23 位。描述符的第 32~39 位。
unsigned char attribute; // 段属性。描述符的第 40~47 位。
unsigned char segmentLimitHight_attribute2; // 段界限 16~19 位,第 20~23 位是段属性。描述符的第 48~55 位。
unsigned char segmentBaseAddressHigh; // 段基址 24~31 位。描述符的第 46~63 位。
}GlobalDescriptor;

段基址虽然存储在描述符的第 16~39 位 和第 24~31 位两段连续的空间中,但用C语言表示它的时候,却人为地将它拆分成了“低位”、“中位”和“高位”三部分,也就是,把描述符的第 16~39 位拆分成了第 16~31 位和第 32~39 位两段。在C语言中,没有现成的能存储23位的整数类型,却用能存储16位和8位的整数类型。将段基址连在一起的24位拆分,用C语言表示更方便。

C语言中的位域

前面已经用到了位域,那就简单学习一下位域的知识吧。

用两段代码开始。

struct{
unsigned int age;
unsigned int height;
}Person;
struct{
unsigned int age:3;
unsigned int height:4;
}Person2;

第二段代码使用了位域,第一段代码是普通的struct结构。位域语法与struct的差异仅在于声明成员变量的语法不同。

struct结构中,声明成员变量的语法是unsigned int age。在位域中,声明成员变量的语法是unsigned int age:3。后者指定了成员变量使用的bit的数量,是3个,而不是1个字节、8个bit。

第一段代码创建的Person占用8个字节,第二段代码创建的Person2占用4个字节。

抽象出位域的成员变量的声明语法:dataType VariableName:bitCountdataType只能是int系列的整数类型,即只能是intunsigned intsigned int 三种类型,不能是char等类型。这是语法规定。bitCount不能超过8个字节。

nasm汇编

用汇编语言表示描述符,是作者写本文的终极目的,前面的一切都是铺垫和基础。C语言表示描述符,在前面写出来,是因为它是作者理解描述符的汇编代码的大功臣。作者在看描述符的汇编代码前,没有学过汇编语言,所以第一次看描述符的汇编代码时,怎么都理解不了。看了别人写的描述符的C语言代码后,才恍然大悟,突然理解了描述符的的汇编代码。

所以,在前文给出描述符的C代码,一是为了纪念这个大功臣,二是让与曾经看不懂汇编代码的作者一样的读者也能借住C代码理解汇编代码。当然,可能是作者多虑了,读者朋友才不会像作者这么愚钝呢。

不使用宏

第一个问题,创建一个描述符,例如DESC_VIDEO,语法是什么样的。

第二个问题。描述符的实质是段基址、段界限和段属性。是直接用代码堆砌出描述符呢还是根据给定的段基址、段界限和段属性经过运算拼凑出描述符?

先解答第二个问题。直接用代码堆砌出描述符的汇编代码如下:

DESC_VIDEO	dw	3120h																											; 描述符的第 0~15 位
dw 111Fh ; 描述符的第 16~31 位
db EFh ; 描述符的第 32~39 位
db 42h ; 描述符的第 40~47 位
db 00h ; 描述符的第 48~55 位
db FFh ; 描述符的第 56~63 位

与前面的C代码比较,每行对应一个struct的成员变量。从上面的汇编代码能看出段基址、段界限和段属性是什么吗?看不出来,需要计算。而且,总不能拿到给定的段基址、段界限和段属性后,先将它们转换成二进制,然后再分割填到上面的代码中吧?最好是给定段基址、段界限和段属性后,经过一段代码处理,就自动构建了描述符。这就是下面要写的方式。

汇编中的宏类似C语言中的函数,给定参数,函数会完成一些功能。这个宏接收段基址、段界限和段属性,然后生成描述符。

宏的语法是什么样的?创建一个宏的模板是:

%macro macroName paramCount
;some code
;some code
%endmacro

创建描述符的宏是:

; 三个参数依次是:base(段基址)、limit(段界限)、attribute(段属性)
; 在宏中需用到这三个参数时,对应的代号分别是:%1、%2、%3。
; base--32位,limit--20位,attribute--12位
%macro Descriptor 3
dw %2 & FFFFh ; 段界限的第 0~15 位。16位
dw %1 & FFFFh ; 段基址的第 0~15 位。16位。
db (%1 & FF0000h) >> 16 ; 段基址的第 16~23 位。8位。
db %3 & FFh ; 段属性的第 0~7 位。48位。
db (%2 & F0000) | (%3 >> 8) ; 段界限的第 16~20 位 和 段属性的第 8~11 位。56位。
db %1 >> 24 ; 段基址的第 24~31 位。8位。
%endmacro

使用这个宏创建一个描述符,代码如下:

DESC_VIDEO:	Descriptor	0B8000h		0ffff			0

段属性是随便设置的。描述符的段属性比较复杂。作者暂时没有弄清楚。

操作系统中的描述符和GDT的更多相关文章

  1. python2.7高级编程 笔记二(Python中的描述符)

    Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解.这些特性包括列表/集合/字典推导式,属性(property).以及装饰器(decorator).对于大部分特性来说,这些" ...

  2. Linux中文件描述符fd和文件指针flip的理解

    转自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html 简单归纳:fd只是一个整数,在open时产生.起到一个索引的作用,进程通 ...

  3. [转载] linux中文件描述符fd和文件指针flip的理解

    转载自http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html 简单归纳:fd只是一个整数,在open时产生.起到一个索引的作用,进程通 ...

  4. LINUX中文件描述符传递

    body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...

  5. linux中文件描述符

    :: # cat ping.txt PING baidu.com (() bytes of data. bytes from ttl= time=32.1 ms bytes from ttl= tim ...

  6. 对于Linux中文件描述符的疑问以及解决

    问题 ​ 每次web服务器或者是几乎所有Linux服务器都需要对文件描述符进行调整,我使用ulimit -n来查看当前用户的最多能打开的文件,默认设置的是1024个,但是系统运行起来以及开启一些简单的 ...

  7. 详解python中的描述符

    描述符介绍 总所周知,python声明变量的时候,不需要指定类型.虽然现在有了注解,但这只是一个规范,在语法层面是无效的.比如: 这里我们定义了一个hello函数,我们要求name参数传入str类型的 ...

  8. 聊聊Python中的描述符

    描述符是实现描述符协议方法的Python对象,当将其作为其他对象的属性进行访问时,该描述符使您能够创建具有特殊行为的对象. 通常,描述符是具有“绑定行为”的对象属性,其属性访问已被描述符协议中的方法所 ...

  9. 数据段描述符和代码段描述符(一)——《x86汇编语言:从实模式到保护模式》读书笔记10

    一.段描述符的分类 在上一篇博文中已经说过,为了使用段,我们必须要创建段描述符.80X86中有各种各样的段描述符,下图展示了它们的分类. 看了上图,你也许会说:天啊,怎么这么多段描述符啊!我可怎么记住 ...

随机推荐

  1. jupyter安装插件Nbextensions,实现代码提示功能(终极方法)

    jupyter安装插件,实现代码提示功能 第一步 pip install jupyter_contrib_nbextensions -i https://mirrors.tuna.tsinghua.e ...

  2. 机器学习算法-PCA降维技术

    机器学习算法-PCA降维 一.引言 在实际的数据分析问题中我们遇到的问题通常有较高维数的特征,在进行实际的数据分析的时候,我们并不会将所有的特征都用于算法的训练,而是挑选出我们认为可能对目标有影响的特 ...

  3. SpringBoot配置文件(2)

    六.配置文件加载 SpringBoot 启动会扫描以下位置的application.properties或者application.yml文件作为SpringBoot的默认配置文件 file:./co ...

  4. 天梯赛练习 L3-006 迎风一刀斩 (30分) 几何关系

    题目分析: 对于给出的两个多边形是否可以组成一个矩形,这里我们分以下几种情况讨论 1.首先对于给出的两个多边形只有3-3,3-4,3-5,4-4才有可能组成一个矩形,并且两个多边形只可能是旋转90,1 ...

  5. 【Linux】CentOS7中yumbackend.py进程的结束方法

    环境: CentOS Linux release 7.3.1611 (Core) 今天启动这个不怎么用的机器,才启动,就发现后台的yum无法进行安装,持续报这个错误 Loaded plugins: f ...

  6. 【Linux】cron

    每五分钟执行  */5 * * * * 每小时执行     0 * * * *      0 */1 * * *   效果相同 每天执行        0 0 * * * 每周执行       0 0 ...

  7. LeetCode202. 快乐数

    题目 编写一个算法来判断一个数 n 是不是快乐数. 快乐数定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1, 也可能是 无限循环 但始终变不到 ...

  8. Unsafe Fileupload - Pikachu

    概述: 文件上传功能在web应用系统很常见,比如很多网站注册的时候需要上传头像.上传附件等等.当用户点击上传按钮后,后台会对上传的文件进行判断 比如是否是指定的类型.后缀名.大小等等,然后将其按照设计 ...

  9. 一句话木马拿下webshell

    1.我们先建立一个简单的一句话木马文件,我们这里就命名为shell2吧. 2.因为提交的文件可能是有过滤的,我们这个靶场的这个题目就是禁止上传危险的文件类型,如jsp jar war等,所以就需要绕过 ...

  10. uni-app开发经验分享十二: Android平台应用启动时读写手机存储、访问设备信息(如IMEI)等权限策略及提示信息

    Android平台从6.0(API23)开始系统对权限的管理更加严格,所有涉及敏感权限都需要用户授权允许才能获取.因此一些应用基础业务逻辑需要的权限会在应用启动时申请,并引导用户允许. 读写手机存储权 ...