---- 全局描述符表GDT 全局描述符表GDT(Global Descriptor Table)在整个系统中,全局描述符表GDT只有一张,GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口, GDT的入口来访问GDT了。 GDT表在内存中的起始地址,表长度指明GDT表的字节长度值。 我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。 ---- 实例 1:访问GDT 当TI=0时表示段描述符在GDT中,如上图所示: ①先从GDTR寄存器中获得GDT基址。 ②然后再GDT中以段选择器高13位位置索引值得到段描述符。
二、详解 先说明一下概念 (1)全局描述符表GDT(Global Descriptor Table)在整个系统中,全局描述符表GDT只有一张(一个处理器对应一个GDT),GDT可以被放在内存的任何位置, 但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器 ,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。 我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图 ? 局部描述符表LDT LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。 三、实例(对理解非常有用) 1:访问GDT ? 段描述符在GDT中 当TI=0时表示段描述符在GDT中,如上图所示: ①先从GDTR寄存器中获得GDT基址。
切换 GDT 切换 GDT 的工作主要分两个步骤: 通过 sgdt 指令获取当前 gdtr 寄存器存储的 loader 的 GDT 存储空间首地址与界限 创建属于 kernel 的新的 GDT 存储空间 unsigned char gdt_ptr[6]; /* 0~15:Limit 16~47:Base */ DESCRIPTOR gdt[GDT_SIZE]; void copy_gdt int*)(&gdt_ptr[2]); /* 将 LOADER 中的 GDT 复制到新的 GDT 中 */ memcpy(&gdt, (void*)(*p_gdt_base), *p_gdt_limit extern gdt_ptr sgdt [gdt_ptr] ; cstart() 中将会用到 gdt_ptr call copy_gdt ; 在此函数中改变了gdt_ptr ,让它指向新的GDT lgdt [gdt_ptr] ; 使用新的GDT 4.5.
内核知识第六讲,内核编写规范,以及获取GDT表 一丶内核驱动编写规范 我们都知道,在ring3下,如果我们的程序出错了.那么就崩溃了.但是在ring0下,只要我们的程序崩溃了.那么直接就蓝屏了 四丶设置内核代码运行的CPU在那个核心上跑.并获取出来每个核心的GDT表. nCount & shift);//设置在那个线程跑 __asm { sgdt szGDT } KdPrint(("limit:%p GDT
然而此时还需要一个针对全局的,对计算机运行状态进行控制的全局描述符表GDT(Global Descriptor Table)在整个系统中,全局描述符表GDT只有一张(一个处理器对应一个GDT),GDT可以被放在内存的任何位置 unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; #ifndef FRTOS_GDT_H #define FRTOS_GDT_H #include "types.h" class GlobalDescriptorTable { public: class SegementDescriptor GlobalDescriptorTable(); uint16_t CodeSegementSelector(); uint16_t DataSegementSelector(); }; #endif gdt.cpp #include "gdt.h" #include "types.h" GlobalDescriptorTable::GlobalDescriptorTable() : nullSegementDescriptor
网上搜索了一圈,才觉得查看GDT、IDT、TSS这些应该是属于内核调试的。 cosmoslife/article/details/48785683)(话说这个博客的头像有点眼熟啊,会不会我上次那个安装bochs就是参考这位大牛的……) 一开始看这个博客没怎么看懂,不知道显示指定范围和查看GDT NextThread: 00000000 IdleThread: 80553840 DpcQueue: 仔细看一下,会发现里面有显示IDT、GDT 、TSS,那时候我以为这个就算查看到了吧,但是感觉又有点不相信,接着往下看,第一次看的时候没看懂下面的dd是干什么,后来好像有点懂了,这里dd后面加的是8003f000,这不就是GDT后面的值嘛,应该是它的地址了 ,那既然查看GDT是这样,那查看IDT、TSS不是也可以dd后面加它们对应的后面的数字就可以了嘛。
一丶段描述符 1.1 GDT与LDT 1.1.1 段描述符之GDT表 与 LDT表的概述 GDT表 查询inter手册可以得知. 当我们在保护模式下. 进行内存访问的时候 所有的内存访问. 一个全局或者局部标志 决定了 段选择子 是指向GDT 还是 LDT. 想要访问GDT 或者LDT 要提供 段选择子以及偏移地址. 以上就是对GDT表或者 LDT表的描述 总结来说 GDT或者LDT 就是一块内存. 也可以看成一个数组. 根据长度可以确定一个GDT表示的内存有多大 2.GDT是一个数组.数组里面保存的是段描述符结构 请不要搞混概念 在这里我们就可以用驱动程序来读取 GDT了.并且我们进行打印输出即可. 3.1.3 LDTR 总结与实验 总结1 GDT的操作 1.1 获取GDT数组表.输出所有段描述符 遍历获取GDT表 只是获取GDT表的首地址.
内存中只能有一个 GDT,但却可以存在多个 LDT,如上图所示,每个 LDT 作为 GDT 中一个描述符描述的内存段。 通常,一个 LDT 用于划分一个特定任务执行过程中需要使用的内存分段。 3. 初始化 GDT 中指向 LDT 的描述符的段基址及 LDT 代码描述符段基址 ; 初始化 LDT 在 GDT 中的描述符 xor eax, eax mov ax, ds shl eax, 4 add eax 由于 GDT 描述的内存对于所有进程来说都是可见的,且具有相同权限,如果需要为每个进程单独定义权限,也可以通过定义 LDT 的方式来解决,具体做法是创建 LDT 描述符,定义独立的属性,但内存指向 GDT --------------------- [SECTION .gdt] ; GDT ; 段基址, 段界限, ; GDT界限 dd 0 ; GDT基地址 ; ------------------ GDT 选择子 ----------------- SelectorNormal
在start.c中,我们成功的把gdt_ptr的值修改了,让它的基地址字段等于在start.c中定义的gdt数组变量。 memcpy把在loader.asm中定义的GDT表复制给gdt数组了。 ] ; cstart() 中将会用到 gdt_ptr call cstart ; 在此函数中改变了gdt_ptr,让它指向新的GDT lgdt [gdt_ptr 复制到新的 GDT 中 */ memcpy(&gdt, /* New GDT */ (void*)(*((u32*)(&gdt_ptr[2 */ u16* p_gdt_limit = (u16*)(&gdt_ptr[0]); u32* p_gdt_base = (u32*)(&gdt_ptr[2]); *p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR) - 1; *p_gdt_base = (u32)&gdt; disp_str("-----\"cstart\" ends
创建 GDT 描述符 在 GDT 中,我们依次写入数据段与堆栈段的描述符: LABEL_DESC_DATA: Descriptor 0, DataLen-1, 92h - LABEL_GDT 3. - LABEL_GDT 4.3. ; GDT界限 dd 0 ; GDT基地址 ; ------------------ GDT 选择子 ----------------- SelectorNormal - LABEL_GDT SelectorStack equ LABEL_DESC_STACK - LABEL_GDT SelectorVideo
vstart=LOADER_BASE_ADDR LOADER_STACK_TOP equ LOADER_BASE_ADDR jmp loader_start ; 构建全局描述符表和内部段描述符 GDT_BASE equ $ - GDT_BASE ; 获取GDT界限 GDT_LIMIT equ GDT_SIZE - 1 times 60 db 0 SELECTOR_CODE equ (0x0001 << 3 ) + TI_GDT + RPL0 SELECTOR_DATA equ (0x0002 << 3) + TI_GDT+ RPL0 SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0 gdt_ptr dw GDT_LIMIT ;写入GDT界限 dd GDT_BASE ;写入GDT起始内存地址 loadermsg db '2 loader in real 加载GDT lgdt [gdt_ptr] ;3. cr0第0位置1 mov eax, cr0 or eax, 0x00000001 mov cr0, eax
系统维护了一个全局的数据结构叫GDT( Global Descriptor Table),他保存了所有进程的代码段数据段的一些信息。系统有专门的寄存器保存了GDT的地址,叫GDTR。 GDT布局如下。 ? GDT每个项(GDT描述符)对应的结构体是 ? 我们回顾task_struct结构,看到有两个属性desc_struct,和tss_struct。 他是根据进程号(进程id)计算出在GDT中的索引。可参考上图。 // nr是进程id,计算进程的ldt结构在gdt中的索引,执行该进程的时候,从GDT的第tss->ldt项中取得进程的信息。 执行进程的时候,根据进程号,算出tss在gdt的索引,然后把索引里指向的tss里的上下文也加载到对应的寄存器,tss信息中的ldt索引首先从gdt找到进程ldt结构体数据的首地址,即desc_struct
在 GDT 中创建 TSS 对应的描述符与选择子 3.2.1. 创建描述符 在 GDT 中添加 Ring3 堆栈段描述符。 --------------------- [SECTION .gdt] ; GDT ; 段基址, 段界限, ; GDT界限 dd 0 ; GDT基地址 ; ------------------ GDT 选择子 ----------------- SelectorNormal - LABEL_GDT SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT SelectorData equ
结束 GdtLen equ $ - LABEL_GDT ; GDT长度 GdtPtr dw GdtLen - 1 ; GDT界限 dd 0 ; GDT基地址 ; GDT 选择子 SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT ; END of [SECTION .gdt] 段式存储的机制是通过段寄存器和GDT中的描述符共同提供的。 如何实现由实模式到保护模式的转换 1、准备GDT 初始化GDT中各个描述符的信息。 结束 ; GDT 选择子 ;...
是一个表格中表项的号码,这个表,有可能是全局描述符表GDT,也有可能是局部描述符表LDT。 那到底是哪个表?是由段选择子从低到高的第三位来决定的,如果这一位是0,则是GDT,否则就是LDT。 特权级:Ring3 十六进制:0023 二进制:0000000000100 0 11 段序号:4 表类型:GDT 特权级:Ring3 也就是说,cs段指向的是GDT中的第3个表项,其他三个寄存器指向的是 GDT中的第4个表项。 接下来,我们来看一下这个神秘的GDT里面的内容到底是什么?很多人学了内存管理,可能还从来没看过真实的GDT里面到底是什么数据吧。 特权级:Ring3 十六进制:002B 二进制:0000000000101 0 11 段序号:5 表类型:GDT 特权级:Ring3 Linux下我没有找到可以直接用什么命令或者工具查看GDT的方式
Core", libraryDependencies += scalaTest % Test, ) ## 覆盖一下jar包 dependencyOverrides ++= Seq( "gdt.api " % "gdt-api-aaa" % "1.0.6", "org.apache.commons" % "commons-compress" % "1.9", "gdt.api" % "api-client-core " % "2.2.6", "gdt.api" % "api-client-l5" % "1.1.2", "gdt.api" % "gdt-api-adservice" % "2.0.75", "com.alibaba" % "fastjson" % "1.2.67", "gdt.api" % "gdt-api-customer" % "1.0.69", "com.fasterxml.jackson.core " % "jackson-databind" % "2.8.11", "com.tencent.tdbank" % "TDBusSDK" % "1.2.17", "gdt.infra" % "infra-l5
然后 OS 利用这个段索引号,去查找 GDT 表,从而找到每一个段的基地址以及其他一些保护信息。 这个准备工作中,最重要的就是:建立 GDT 这个表,并且把 GDT 的开始地址,存储到寄存器 GDTR 中。 确定 GDT 的地址 在创建段描述符之前,需要先确定: 把 GDT 表放在内存中的什么位置? 暂且就把它放在 0x0001_0000 这个地址吧,距离零地址 64K 的位置。 拿代码段寄存器 cs 来说:当执行一条指令的时候,如果它与上一条指令中的段索引号不同,才会根据新的段索引号到 GDT 中查找相应的段描述符表项。 对段寄存器本身的保护 当逻辑地址中段寄存器的索引号改变时,就会根据新的索引号,到 GDT 中去查表。 当然了,这个索引号不能超过 GDT 的界限。
--------------------- [SECTION .gdt] ; GDT ; 段基址, 段界限, ---------------- GdtLen equ $ - LABEL_GDT ; GDT长度 GdtPtr dw GdtLen - 1 ; GDT界限 dd 0 ; GDT基地址 ; ------------------ GDT 选择子 ----------------- SelectorNormal - LABEL_GDT SelectorPageTbl equ LABEL_DESC_PAGE_TBL - LABEL_GDT SelectorCode32 equ ; 计算出 GDT 基地址的物理地址 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址 ;
内核知识第八讲,PDE,PTE,页目录表,页表的内存管理 一丶查看GDT表. 我们通过WinDbg + 虚拟机可以进行双机调试.调试一下看下GDT表 我们知道,GDT表中.存储的是存储段信息. 保存了一系列的段和内存的属性. 但是微软并没有使用. 当作GDT表的下标.进行查表. 查询GDT表. 011 查询出来下表为3,那么去GDT表的第三项进行查找. 而我们GDT表,微软没有使用它来进行进程隔离. PS: 微软因为不使用GDT表进行进程隔离,所以段选择子都是一样的.基地址都是0,我们的虚拟地址就是线性地址.
GDT 定义 上一篇文章中,我们看到了 GDT 描述符的结构: 根据该结构,我们可以定义一个宏: %macro Descriptor 3 dw %2 & 0FFFFh --------------------- [SECTION .gdt] ; GDT ; 段基址, 段界限, ---------------- GdtLen equ $ - LABEL_GDT ; GDT长度 GdtPtr dw GdtLen - 1 ; GDT界限 dd 0 ; GDT基地址 ; ------------------ GDT 选择子 ----------------- SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT ; -----------