《30天自制操作系统》学习笔记(五)

来源:互联网 发布:怎么ping网络端口 编辑:程序博客网 时间:2024/04/27 16:29
GDT
(一)什么是GDT?需要解决什么问题?
    1.什么是分段?将合计4GB的内存分成很多块,每一块的起始地址都看作0来处理,像这样分割出来的块,就称为段(Segment)。
    2.为什么要引入分段?为了让程序在内存中自由浮动而又不影响它的正常运行,处理器将内存划分成逻辑上的段,并在指令中使用段内偏移地址。
    3.由分段的问题--如何使每个段可以访问?利用段描述符(Segment Descriptor),8个字节,用于存储与一个段有关的信息,每个段都需要一个这样的描述符。
    4.由段描述符引出的问题--段描述符如何存储与使用?存储:在内存中开辟一段空间,在这段空间里,所有的描述符都是挨在一起,集中存放的,这样就构成了一个描述符表(Descriptor Table)。
    5.其中用于存放全局描述符的表就称为全局描述符秒(Global Descriptor Table,GDT),这个表示为整个软硬件系统服务的,在进入保护模式之前,必须要定义全局描述符表。
    6.继续引出的问题--CPU如何查找GDT中的信息?利用了一个叫GDTR的寄存器(注意此处是寄存器,不是内存了,需要和GDT区别对待)。GDTR中高32位表示GDT在内存中的基地址,低16位表示表界限,所以就明确的给出了GDT在内存中的存储信息。

    7.有了GDTR和GDT后,可以方便的找出GDT,但是由此又出现一个问题--如何找出GDT中的某一个段描述符?因为段描述符有8个字节,但是寄存器只有2个字节,所以引入了段选择符(Segment selector),用于寻找段描述符。段选择符有16位,其中低3位为标志位,高13位用于表示GDT的索引号,所以一个GDT中最高可以有8192个段描述符。
(RPL为Reuqest Privilege Level,表示请求特权级)
所以过程如图
(二)什么是段描述符和段选择子?
    1.段描述符
        GDT的组成单元,表示段的信息,主要包含以下信息
            a.段的大小是多少?
            b.段的起始地址在哪里?
            c.段的管理属性
主要属性字段:
    G--粒度标志,G=0以字节为单位,否则以4Kb为单位
    D/B--DB标志,当描述符指向的是可执行代码段时,这一位叫做D位,D=1使用32位地址和32/8位操作数,D=0使用16位地址和16/8位操作数。如果指向的是向下扩展的数据段,这一位叫做B位,B=1时段的上界为4GB,B=0时段的上界为64KB。如果指向的是堆栈段,这一位叫做B位,B=1使用32位操作数,堆栈指针用ESP,B=0时使用16位操作数,堆栈指针用SP。
    AVL标志,目前忽略
    P--存在标志,P=0表示当前段不在主存中
    DPL--描述符特权级。特权级,0为最高特权级,3为最低,表示访问该段时CPU所需处于的最低特权级。如DPL=0则只有CPL为0才可以访问,DPL=3则任意CPL都可以访问(CPL代表CPU当前的特权级)
    S--系统标志。S=1表示系统段,否则是普通的代码段或数据段。
设置代码示例:
//用于设置段描述信息的变量,包括要设置的段描述符的信息
struct SEGMENT_DESCRIPTOR {
    short limit_low, base_low;
    char base_mid, access_right;
    char limit_high, base_high;
};

void init_gdt(void)
{
    struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000;  //GDT起始地址,可自行设定
    int i;

    /* 初始化,8192个全0的段描述符 */
    for (i = 0; i < 8192; i++) {
        set_segmdesc(gdt + i, 0, 0, 0);
    }
    set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);    //设置1号描述符
    set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a);    //设置2号描述符
    load_gdtr(0xffff, 0x00270000);    //载入GDTR

    return;
}

//根据段描述的结构设置对应属性,利用结构体变量的存储结构进行按位设置
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
    if (limit > 0xfffff) {
        ar |= 0x8000; /* G_bit = 1 */
        limit /= 0x1000;
    }
    sd->limit_low    = limit & 0xffff;
    sd->base_low     = base & 0xffff;
    sd->base_mid     = (base >> 16) & 0xff;
    sd->access_right = ar & 0xff;
    sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);
    sd->base_high    = (base >> 24) & 0xff;
    return;
}

2.段选择子
    实模式下的6个段寄存器CS、DS、ES、FS、GS、SS,在保护模式下叫段选择器。在保护模式下,传送到段选择器的内容不是逻辑段地址,而是段描述符在GDT中的索引号(段选择子)
    使用示例如下:                                 段基址         段界限        段属性
    LABEL_GDT                    Descriptor    0,               0,           0            ;空描述符
    LABEL_DESC_TEST         Descriptor    0,         0ffffh,          属性值
    Descriptor为一个实现   set_segmdesc函数功能的标识

    Selector_test            equ    LABEL_DESC_TESR-LABEL_GDT
    Selector_test就与LABEL_DESC_TEST建立了对应关系,使用时可以用如下方式:
    jmp Selector_test:0
或者
    mov ax,Selector_test
    mov gs,ax
等方式调用

(三)几个对应关系
1.GDT与GDTR的关系
GDT位于内存中,用于存储段描述符;GDTR是寄存器,用于存储GDTR的信息。GDT像是书中某一个章节的具体内容,有起始页码,结束页码和内容等;GDTR像是对应的目录,记录了GDTR的起始页码,页面范围。
2.段描述符和段选择符的关系
段描述符用于记录一个段的具体信息,就像是GDT这个大章中的某一个小节;段选择符则记录这个小结对应的页码(位置),以方便查找。
(四)几个不清楚问题
1.GDT和GDTR都有对应的存储位置,那么段选择符又是存储在哪里?
0 0
原创粉丝点击