您的位置:首页 > 其它

自己动手写操作系统

2012-10-21 12:31 288 查看
《自己动手写操作系统》读后感http://blog.csdn.net/zgh1988/article/details/7059936

全面剖析《自己动手写操作系统》第一章http://blog.csdn.net/zgh1988/article/details/7060032

全面剖析《自己动手写操作系统》第二章http://blog.csdn.net/zgh1988/article/details/7062065

[html]view
plaincopy

1;==========================================

2;pmtest1.asm

3;编译方法:nasmpmtest1.asm-opmtest1.com

4;==========================================

5

6%include"pm.inc";常量,宏,以及一些说明

7

8org0100h

9jmpLABEL_BEGIN

10

11[SECTION.gdt]

12;GDT

13;段基址,段界限,属性

14LABEL_GDT:Descriptor0,0,0;空描述符

15LABEL_DESC_CODE32:Descriptor0,SegCode32Len-1,DA_C+DA_32;非一致代码段,32

16LABEL_DESC_VIDEO:Descriptor0B8000h,0ffffh,DA_DRW;显存首地址

17;GDT结束

18

19GdtLenequ$-LABEL_GDT;GDT长度

20GdtPtrdwGdtLen-1;GDT界限

21dd0;GDT基地址

22

23;GDT选择子

24SelectorCode32equLABEL_DESC_CODE32-LABEL_GDT

25SelectorVideoequLABEL_DESC_VIDEO-LABEL_GDT

26;ENDof[SECTION.gdt]

27

28[SECTION.s16]

29[BITS16]

30LABEL_BEGIN:

31movax,cs

32movds,ax

33moves,ax

34movss,ax

35movsp,0100h

36

37;初始化32位代码段描述符

38xoreax,eax

39movax,cs

40shleax,4

41addeax,LABEL_SEG_CODE32

42movword[LABEL_DESC_CODE32+2],ax

43shreax,16

44movbyte[LABEL_DESC_CODE32+4],al

45movbyte[LABEL_DESC_CODE32+7],ah

46

47;为加载GDTR作准备

48xoreax,eax

49movax,ds

50shleax,4

51addeax,LABEL_GDT;eax<-gdt基地址

52movdword[GdtPtr+2],eax;[GdtPtr+2]<-gdt基地址

53

54;加载GDTR

55lgdt[GdtPtr]

56

57;关中断

58cli

59

60;打开地址线A20

61inal,92h

62oral,00000010b

63out92h,al

64

65;准备切换到保护模式

66moveax,cr0

67oreax,1

68movcr0,eax

69

70;真正进入保护模式

71jmpdwordSelectorCode32:0;执行这一句会把SelectorCode32装入cs,并跳转到Code32Selector:0处

72;ENDof[SECTION.s16]

73

74

75[SECTION.s32];32位代码段.由实模式跳入.

76[BITS32]

77

78LABEL_SEG_CODE32:

79movax,SelectorVideo

80movgs,ax;视频段选择子(目的)

81

82movedi,(80*10+0)*2;屏幕第10行,第0列。

83movah,0Ch;0000:黑底1100:红字

84moval,'P'

85mov[gs:edi],ax

86

87;到此停止

88jmp$

89

90SegCode32Lenequ$-LABEL_SEG_CODE32

91;ENDof[SECTION.s32]

在下面我要讲述3.1中所遇到的问题和迷惑。主要以pmtest1.asm为例,从以下几方面进行分析和讲解。

1)[SECTION.XXX]为何物?

2)全局描述符表(GDT)、描述符(Descriptor)、全局描述符表寄存器(GDTR)、选择子(SelectorXXX)是什么?用来做什么?

3)实模式下的寻址方式与保护模式下的寻址方式的区别?

4)描述符宏定义和初始化段描述符

5)为加载gdtr做准备工作

6)其他

1、[SECTION.XXX]为何物?

SECTION和SEGMENT的作用相类似,就是代表“段”的意思,从整个程序来看,该程序分为3个模块,分别是[SECTION.gdt]、[SECITON.s16]、[SECTION.s32]三部分。我们很容易就可以看出,其中的[SECTION.gdt]应该是数据段,其他的两个是代码段。通过[SECTION.XXX]将程序分成不同模块,完成不同的功能,使得程序看起来清晰明了。

2、描述符(Descriptor)、全局描述符表(GDT)、全局描述符表寄存器(GDTR)、选择子(SelectorXXX)是什么?用来做什么?






	段,在80X86中,分段机制将内存空间分成一个或者多个线性区域,我们把这些线性区域称为段。我们需要将这些段区分开来,于是分段机制为每个段赋予3个属性,分别是:1、段基址(Baseaddress):指定段在线性地址空间中的开始地址。2、段界限(Limit):表示了段内最大可用偏移量,也就是说它定义了段的长度。3、段属性(Attribute):指定了段的特性,包括:可读,可写或者可执行,特权等级等特性。

段描述符(Descriptor),在程序中,我们需要定义一个数据结构来表示段,它同样包含三个元素,段基址(Base),段界限(Limit),段属性(Attribute),我们称它为段描述符(Descriptor)。段描述符和段就是同一个概念,每个段描述符要占用8个字节的空间。
	段描述符表(DescriptorTable),在一个程序中,不只存在一个段(段描述符)。所以我们需要将这些段描述符组织起来,于是定义了一个存储段描述符的数组,称为段描述符表。
段选择子(SelectorXXX),把所有段描述符都存储在段描述符表中,当我们使用其中某一个段的时候,我们并不直接指向该段,而是通过该段描述符在段描述符表中的位置来访问的。故段选择子,就是一个16位的标识符,用来标识该段描述符在描述符表中的位置。


	段描述符表可以分为两类,一类是全局描述符表GDT(GlobalDescriptorTable),一类是局部描述符表LDT(LocalDescriptorTable)。系统中供所有的任务共享的是全局描述符表,而不同的任务却是使用自己的局部描述符表。
紧接着,如何让系统知道段描述符表在什么地方呢?处理器提供了内存管理寄存器,分别是全局描述符表寄存器(GDTR)、局部描述符表寄存器(LDTR)。GDTR寄存器中用于存放全局描述符表GDT的32位线性基地址和16位的表的长度值。LDTR寄存器中用于存放局部描述符表LDT的32位线性基地址和16位的表的长度值。通过系统指令,lgdt将GDT的线性基址和长度值加载到GDTR寄存器中,lldt将LDT的线性基址和长度值加载到LDTR寄存器中。
下面我们来分析程序中的代码:
[html]viewplaincopy11[SECTION.gdt]
12;GDT
13;段基址,段界限,属性
14LABEL_GDT:Descriptor0,0,0;空描述符
15LABEL_DESC_CODE32:Descriptor0,SegCode32Len-1,DA_C+DA_32;非一致代码段,32
16LABEL_DESC_VIDEO:Descriptor0B8000h,0ffffh,DA_DRW;显存首地址
17;GDT结束
18
19GdtLenequ$-LABEL_GDT;GDT长度
20GdtPtrdwGdtLen-1;GDT界限
21dd0;GDT基地址
22
23;GDT选择子
24SelectorCode32equLABEL_DESC_CODE32-LABEL_GDT
25SelectorVideoequLABEL_DESC_VIDEO-LABEL_GDT
26;ENDof[SECTION.gdt]
在程序中,14、15、16行定义了3个段描述符,LABEL_GDT(空描述符),LABEL_DESC_CODE32(32位代码段描述符),LABEL_SESC_VIDEO(显示内存描述符)。每个描述符都包含了3个属性,段基址、段界限、段属性。
将三个描述符组织到一起构成一个全局段描述符表(GDT)。12-17行完成了GDT的定义。
GdtLen为GDT的长度。
	GdtPtr为一个数据结构,里面包含两个元素,第一个元素是2bytes的GDT界限。第二个元素是4bytes的GDT的基地址。该数据结构与全局描述符表寄存器(GDTR)的数据结构相同,所以在加载GDTR的时候(源代码55行),就是将该GdtPtr加载到GDTR中。
由于第一个段LABEL_GDT是空描述符,它仅仅代表该GDT的初始地址,所以该描述符为空描述符,一般情况下,不为它创建选择子。然后该程序建立了两个选择子(24、25行)SelectorCode32和SelectorVideo,分别对应着这两个段LABEL_SESC_CODE32和LABEL_DESC_VIDEO。
这段代码的结构大家应该明白了吧,下面我要分别介绍段描述符,全局描述符表寄存器,选择子的数据结构。
段描述符(Descriptor)的结构图如下:


段描述符占有8个字节,在这里我只想提醒大家一下,段基址分别占有BYTE2、BYTE3、BYTE4和BYTE7。在下面初始化段描述符的时候需要用到这些。
段选择子的结构图如下图所示:


在这里简单的介绍一下,它使用(15…3)来表示索引,故每一个描述符表最多只用213个描述符,除去第一个空描述符,则可以使用的描述符为8191个描述符。
TI标志着该选择子所指向的段描述符是全局描述符,还是局部描述符。当TI=0时,表示全局描述符,当TI=1时,表示局部描述符。
RPL请求优先级,稍后下一节将会提到。
全局描述符表寄存器(GDT)的结构图如下所示:


在这里,你可以和上面程序中的GdtPtr数据结构做比较,是不是格式相同。2个字节表示GDT界限,4个字节表示GDT基地址。
3、实模式下的寻址方式与保护模式下的寻址方式有什么不同?
	在实模式下,也就是在8086系统下的寻址方式。Intel8086是16位的CPU,它有着16位的寄存器(Register),16位的数据总线(DataBus)以及20位的地址总线(AddressBus)和1MB的寻址能力。一个地址是由段和偏移两部分组成的,物理地址遵循这样的计算公式:
	物理地址(PhysicalAddress)=段值(Segment)*16+偏移(Offset)
其中段值和偏移都是16位的。故寻址范围为1MB。
在保护模式下,有了分段机制,所以它的寻址方式发生了很大的变化。具体如下图所示:


	在保护模式下,首先使用段选择子在段描述符表中查找到相对应的段描述符,找到32位段基址,然后在与32位的偏移量相加,得到线性地址。段基址和段偏移量都是32位的,所以寻址范围大小为4GB。在程序中jmpdwordSlectorCode32:0的作用,就是进入保护模式下的寻址方式。其中,在使用某个段时,它的段选择子是存储在段寄存器中的。
这里面存在着一个问题,是否我们每次寻址都要先去全局描述符表寄存器(GDTR)中,查找到全局描述符表(GDT)的基址,然后再次根据选择子的索引跳转到该描述符所在的位置,然后取得段描述符中的基址,如果这样的话,我们里里外外采访了几次内存,太浪费时间了。实际上段寄存器结构是这样的:


这样的好处就是,我们可以直接获取段描述符。
4、描述符宏定义和初始化段描述符
[html]viewplaincopy1;描述符
2;usage:DescriptorBase,Limit,Attr
3;Base:dd
4;Limit:dd(low20bitsavailable)
5;Attr:dw(lower4bitsofhigherbytearealways0)
6%macroDescriptor3
7dw%2&0FFFFh;段界限1(2字节)
8dw%1&0FFFFh;段基址1(2字节)
9db(%1>>16)&0FFh;段基址2(1字节)
10dw((%2>>8)&0F00h)|(%3&0F0FFh);属性1+段界限2+属性2(2字节)
11db(%1>>24)&0FFh;段基址3(1字节)
12%endmacro;共8字节
2、3、4、5行注释告诉我们,该宏定义需要三个变量,分别是段基址(4bytes),段界限(4bytes),段属性(dw)。 回顾刚才的段描述符结构,该宏定义,就是将变量Base,Limit,Attr分别安插到描述符中相应的位置。Base是1,Limit是2,Attr是3 7是将Limit低16位赋值给描述符的BYTE0和BYTE1 8是将Base低16位赋值给描述符的BYTE2和BYTE3 9是将Base右移16位后的低8位(也就是原Base的第16—23位)赋值给描述符的BYTE4 10是将Limit右移8位之后的第8—11位和Attr的0—7和12—15位,组合起来存储到描述符的BYTE5和BYTE6 11是将Base右移24位后的低8位(也就是原Base的24—32位)赋值给描述符的BYTE7初始化段描述符代码:[html]viewplaincopy37;初始化32位代码段描述符
38xoreax,eax
39movax,cs
40shleax,4
41addeax,LABEL_SEG_CODE32
42movword[LABEL_DESC_CODE32+2],ax
43shreax,16
44movbyte[LABEL_DESC_CODE32+4],al
45movbyte[LABEL_DESC_CODE32+7],ah
为什么要初始化?你会发现这里只是修改了段描述符LABEL_DESC_CODE32的BYTE2,BYTE4,BYTE7。是不是突然恍然大悟?因为在我们初始化该LABEL_DESC_CODE32描述符时,将其基地址初始化为0,所以我们要修改描述符的基地址为其实际的地址。这也是在前面介绍段描述符的时候,我提醒大家需要注意的地方,即描述符的基地址所占有的字节是BYTE2,BYTE4,BYTE7。5、为加载gdtr做准备工作[html]viewplaincopy47;为加载GDTR作准备
48xoreax,eax
49movax,ds
50shleax,4
51addeax,LABEL_GDT;eax<-gdt基地址
52movdword[GdtPtr+2],eax;[GdtPtr+2]<-gdt基地址
53
54;加载GDTR
55lgdt[GdtPtr]
这个很好理解,我们就是对GdtPtr进行赋值,主要是初始化GDT的基地址。也就是将GDT的初始地址,赋值给GdtPtr的BYTE2,BYTE3,BYTE4,BYTE5。使GdtPtr的数据结构刚好符合GDTR,然后执行lgdt[GdtPtr],加载全局描述符表寄存器。将GDT的基地址和界限赋值给GDTR。6、其他 至于接下来的关中断、打开地址线A20、切换到保护模式、进入保护模式,跳转到32位代码段等一系列的问题,可以从书中找到合适的解释。参考书籍:Linux内核完全剖析基于0.12内核
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: