- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
所谓工作模式,是指CPU的寻址方式、寄存器大小、指令用法和内存布局等。
实模式
段基址:段内偏移地址”产生的逻辑地址就是物理地址,即程序员可见的地址完全是真实的内存地址。
保护模式
在保护模式中,内存的管理模式分为两种——段模式和页模式。其中页模式也是基于段模式的。也就是说,保护模式的内存管理模式事实上是:纯段模式和段页式。进一步说,段模式是必不可少的,而页模式则是可选的——如果使用页模式,则是段页式,否则这是纯段模式。
保护模式下的段寄存器 由 16位的选择器
与 64位的段描述符寄存器
构成
PS:原先实模式下的各个段寄存器作为保护模式下的段选择器,80486中有6个(即CS,SS,DS,ES,FS,GS)80位的段寄存器。由选择器CS对应表示的段仍为代码段,选择器SS对应表示的段仍为堆栈段。
全局描述符表GDT(Global Descriptor Table)
在整个系统中,全局描述符表GDT只有一张,GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者提供了一个寄存器GDTR用来存放GDT的入口地址
,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。全局描述符表在系统中只能有一个,且可以被每一个任务所共享.任何描述符都可以放在GDT中,但中断门和陷阱门放在GDT中是不会起作用的.能被多个任务共享的内存区就是通过GDT完成的,
GDTR寄存器中的基地址指定GDT表在内存中的起始地址,表长度指明GDT表的字节长度值。
指令LGDT和SGDT分别用于加载和保存GDTR寄存器的内容。在机器刚加电或处理器复位后,基地址被默认地设置为0,而长度值被设置成0xFFFF。在保护模式初始化过程中必须给GDTR加载一个新值。
局部描述符表LDT(Local Descriptor Table)局部描述符表可以有若干张,每个任务可以有一张。我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图
局部描述符表在系统中可以有多个,通常情况下是与任务的数量保持对等,但任务可以没有局部描述符表.任务间不相干的部分也是通过LDT实现的.这里涉及到地址映射的问题.和GDT一样,中断门和陷阱门放在LDT中是不会起作用的.
LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。
LDTR记录局部描述符表的起始位置,与GDTR不同,LDTR的内容是一个段选择子。
由于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子,LDTR装载的就是这样一个选择子。LDTR可以在程序中随时改变,通过使用lldt指令。如上图,如果装载的是Selector 2则LDTR指向的是表LDT2。举个例子:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。
由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表则可以将每个进程的程序段、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。
当进行任务切换时,处理器会把新任务LDT的段选择符和段描述符自动地加载进LDTR中。在机器加电或处理器复位后,段选择符和基地址被默认地设置为0,而段长度被设置成0xFFFF。GDT表只有一个,是固定的;而LDT表每个任务就可以有一个,因此有多个,并且由于任务的个数在不断变化其数量也在不断变化。如果只有一个LDTR寄存器显然不能满足多个LDT的要求。因此INTEL的做法是把它放在放在GDT中。
与GDTR的作用类似,IDTR寄存器用于存放中断描述符表IDT的32位线性基地址和16位表长度值。指令LIDT和SIDT分别用于加载和保存IDTR寄存器的内容。在机器刚加电或处理器复位后,基地址被默认地设置为0,而长度值被设置成0xFFFF。和GDT一样,中断描述符表在系统最多只能有一个,中断描述符表内可以存放256个描述符,分别对应256个中断.因为每个描述符占用8个字节,所以IDT的长度可达2K.中断描述符表中可以有任务门、中断门、陷阱门三个门描述符,其它的描述符在中断描述符表中无意义。
在保护模式下,段寄存器的内容已不是段值,而称其为选择子.该选择子指示描述符在上面这三个表中的位置,所以说选择子即是索引值。
当我们把段选择子装入寄存器时不仅使该寄存器值,同时CPU将该选择子所对应的GDT或LDT中的描述符装入了不可见部分。这样只要我们不进行代码切换(不重新装入新的选择子)CPU就会不会对不可见部分存储的描述符进行更新,可以直接进行访问,加快了访问速度。一旦寄存器被重新赋值,不可见部分也将被重新赋值。
段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。
他的index(描述符索引)部分表示所需要的段的描述符在描述符表的位置,由这个位置再根据在GDTR中存储的描述符表基址就可以找到相应的描述符。
然后用描述符表中的段基址加上逻辑地址(SEL:OFFSET)的OFFSET就可以转换成线性地址,
段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选择子是在LDT选择。
请求特权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。
关于特权级的说明:任务中的每一个段都有一个特定的级别。每当一个程序试图访问某一个段时,就将该程序所拥有的特权级与要访问的特权级进行比较,以决定能否访问该段。系统约定,CPU只能访问同一特权级或级别较低特权级的段。
例如给出逻辑地址:21h:12345678h转换为线性地址
a. 选择子SEL=21h=0000000000100 0 01b 他代表的意思是:选择子的index=4即100b选择GDT中的第4个描述符;TI=0代表选择子是在GDT选择;左后的01b代表特权级RPL=1
b. OFFSET=12345678h若此时GDT第四个描述符中描述的段基址(Base)为11111111h,则线性地址=11111111h+12345678h=23456789h
TR用于寻址一个特殊的任务状态段(Task State Segment,TSS)。TSS中包含着当前执行任务的重要信息。
TR寄存器用于存放当前任务TSS段的16位段选择符、32位基地址、16位段长度和描述符属性值。它引用GDT表中的一个TSS类型的描述符。指令LTR和STR分别用于加载和保存TR寄存器的段选择符部分。当使用LTR指令把选择符加载进任务寄存器时,TSS描述符中的段基地址、段限长度以及描述符属性会被自动加载到任务寄存器中。当执行任务切换时,处理器会把新任务的TSS的段选择符和段描述符自动加载进任务寄存器TR中。
当TI=0时表示段描述符在GDT中,如上图所示:
①先从GDTR寄存器中获得GDT基址。
②然后再GDT中以段选择器高13位位置索引值得到段描述符。
③段描述符符包含段的基址、限长、优先级等各种属性,这就得到了段的起始地址(基址),再以基址加上偏移地址yyyyyyyy才得到最后的线性地址。
当TI=1时表示段描述符在LDT中,如上图所示:
①还是先从GDTR寄存器中获得GDT基址。
②从LDTR寄存器中获取LDT所在段的位置索引(LDTR高13位)。
③以这个位置索引在GDT中得到LDT段描述符从而得到LDT段基址。
④用段选择器高13位位置索引值从LDT段中得到段描述符。
⑤段描述符符包含段的基址、限长、优先级等各种属性,这就得到了段的起始地址(基址),再以基址加上偏移地址yyyyyyyy才得到最后的线性地址。
如果该位为零,那么内存是逐字节寻址的?如果是1,那么内存是按4Kb×4Kb寻址的? 例如,如果该位设置为 0,并且我寻址内存位置 a000h,那么我将寻址该字节在那个位置对吧?如果我寻址下一个位置a0
这是一个非常愚蠢的问题,但我似乎无法解决它。在我的操作系统中,GDT 是通过与内核链接的汇编代码设置的。当发生这种情况时,当然会在加载 GDT 时设置数据段和代码段。此信息在汇编代码中存储为 GDT_
前言 所谓工作模式,是指CPU的寻址方式、寄存器大小、指令用法和内存布局等。 实模式 段基址:段内偏移地址”产生的逻辑地址就是物理地址,即程序员可见的地址完全是真实的内存地址。 保护模式 在保护模式中
我有一个引导加载程序,它正在为我设置 GDT。是否可以找出这个 GDT 的地址以便我可以使用它? 最佳答案 当然,只需使用 sgdt 指示读取当前设置。 关于assembly - 是否有可能获得 GD
使用 lgdt 初始化 GDT 并将其加载到 GDTR 后,稍后如何更新 GDT? 如果我使用 sgdt 命令获取基地址,然后更新或添加条目,然后使用 lgdt 再次重新加载,我是否正确?还有其他方法
我目前正在使用 x86 Assember,以提高我的低级编程技能。目前,我在 32 位保护模式下的寻址方案遇到了一个小问题。 情况如下: 我在 0x7e0 加载了一个程序,它将 CPU 切换到保护模式
在bootasm.S .p2align 2 # force 4 byte alignment gdt: SEG_NULLASM
flush_gdt: lgdt [gdtr] jmp 0x08:complete_flush complete_flush: mov ax, 0x10 mov ds,
在bootasm.S .p2align 2 # force 4 byte alignment gdt: SEG_NULLASM
GDT如何反射(reflect)在进程的线性空间上(如果我没理解错的话,GDTR包含page和offset)。?或这个问题的另一个版本:GDTR 对于每个进程都是唯一的。? 最佳答案 只有一个 GDT
我正在开发一个小型操作系统,它将为每个进程使用单独的本地描述符表。我知道我需要使用 lldt从我的 GDT 加载 LDT 段的指令。我已经让我的内核以有效的 GDT 以保护模式运行,但我无法弄清楚我的
我主要使用 C++ 制作操作系统,但对于引导加载程序,我使用 FASM。当我尝试设置 GDT 时,Qemu 清除屏幕并在顶部重新打印“SeaBIOS”。它会继续这样循环,直到我关闭它。这是它的动图:
我正在编写一个简单的自制 64 位操作系统,通过 UEFI 启动它。这意味着当我的代码开始执行时,它已经处于长模式,并且启用了分页。 现在,退出 UEFI 引导服务后,我想用我自己的控制结构替换 UE
在我开始之前,这里提到的所有代码都在运行@ring0(内核模式)- OSX 10.9: 以下函数崩溃: UINT64 GetGdtBase() { UINT64 gdt = 0; as
我正在尝试使用 Eclipse (Neon) 在 C++ 中运行一个非常简单的 GUI 应用程序:程序启动,显示红色并在 10 秒后自行关闭。 为了实现这一点,我正在运行 Allegro 5.0.10
这个问题的灵感来自多年来许多人遇到的问题,尤其是在 x86 操作系统开发中。最近一个related NASM question被编辑撞了。在那种情况下,该人使用 NASM 并收到组装时间错误: shi
据我了解,这两个表都包含段描述符,提供每个段的访问详细信息,包括基地址、类型、长度、访问权限...等。 看着这个blog差异描述如下: 1. GDT在系统中只有一份,而LDT可以有多个 2. GDT在
我是系统架构的入门级学生,准确地说是intel x86。目前我正在阅读 Intel 的手册 (1,3a,3b,3c),但我被困在分段部分。 据我所知,在保护模式下,系统正在将逻辑内存转换为线性内存(或
I want to use gdb to see my GDTR/LDTR/TTR and segment register 不可见部分(x86) 所以在 gdb 中我输入“p/x $gdtr”...
我是一名优秀的程序员,十分优秀!