操作系统:保护模式(一)GDT 与分段机制

news/2024/10/1 17:17:40

GDT 与分段机制

CPU开机时运行于实模式,寻址方式是段寄存器 \(\times\) 10+偏移寄存器=物理地址,主要原因是因为 8086 地址线和数据线不匹配导致的。但是这种寻址方式既不安全也不支持现代操作系统所需的、多任务支持、cpu 特权模式等。

在实模式下,对于基址,变址寻址的寄存器有明确要求。在保护模式下,除了 esp 以外的所有通用寄存器均可以用于基址,变址寻址。

在 x86 引入的保护模式下,CPU的32条地址线全部有效,可寻址高达4G字节的物理地址空间。为了维护保护模式所支持的各类信息,同时为了兼容,x86 仍然采用分段的方式划分内存,这些关于内存段的限制信息放在一个叫做 全局描述符表(Global Descriptor Table,全球描述符表)的结构里。全局描述符表中含有一个个表项,每一个表项称为段描述符(Descriptor)。而在保护模式下要生成最终的地址,显然就变成了先到 GDT 里拿段基址,再和偏移地址组合起来。而 GDT 由于存了很多段,所以就需要有个指针指向哪个段,这个指针就是段选择子(Selector),平时放在段寄存器里。

注意:GDT 的第 0 个描述符被保留为无效
目的是为了防止非法的段访问,并提供一种有效的机制来检测和处理无效的段引用。通过这种设计,可以增强系统的安全性和稳定性。

由于历史原因,段的基址与界限等被分割为几个块存放到描述符中。

  1. 段基址(32 位):是该内存段的基地址
  2. 段界限表示段边界的扩张最值,即最大扩展多少或最小扩展多少,用20位来表示,它的单位可以是字节,也可以是 4KB,这是由G位决定的(G为1时表示单位为4KB)。
  3. 段的属性和权限标志,它与界限的高 4 位组合在一起。典型的段属性包括:
    • 段类型(可执行、可读写等)
    • DPL(Descriptor Privilege Level,描述符特权级别)
    • P 位(Present 位,段是否存在)
    • G 位(Granularity,粒度位)
    • AVL 保留备用
    • D/B 位(默认操作大小位,用于指示段是16位模式还是32位模式)

\[实际段界限边界值=(描述符中的段界限+1) \times 段界限的单位大小(即字节或4KB))-1 \]

这样,每个段在GDT中都规定了大小然后选择子选择了段后,只能访问这个段内的内存,CPU 在越界访问会发生异常。达到了保护模式的效果。

段选择子(Selector) 实际上是 GDT 表索引与三位属性的组合:

  • 低 2 位即第 0~1 位, 用来存储 RPL,即请求特权级 ( 0、 1、 2、 3 四种特权级,数字越小权限越大)
  • 第 2 位是 TI 位,即 Table Indicator,用来指示选择子是在 GDT 中,还是 LDT 中索引描述符

于是,x86 的分段寻址机制如图所示:

LDT: 历史的遗留

LDT(Local Descriptor Table,本地描述符表)是 x86 架构中用于内存管理的一个结构,最早是在 x86 保护模式下引入的。它的主要作用是定义任务或进程的内存段。LDT 是 GDT(Global Descriptor Table,全局描述符表)的补充,但它针对的是每个任务或进程的局部内存段定义。

在 x86 的段式内存管理中,内存通过段(segment)来访问。段可以定义代码段、数据段或堆栈段等不同类型的内存区域。每个段有自己的基地址、限制(size),以及访问权限控制。这些段的信息存储在一个描述符表中。GDT 和 LDT 就是用于存储这些段描述符的表。

LDT 的设计是为了支持多任务和进程的隔离。它允许每个任务定义自己的段,使得内存保护和隔离更加灵活。LDT 的选择子可以为 1.

但由于现代操作系统普遍采用扁平内存模型和分页机制,LDT 的使用逐渐减少甚至被废弃。它的功能已被分页机制很好地取代,同时也简化了系统的内存管理和任务切换。

简单的引导程序:进入 32 位保护模式

进入保护模式,需要

  1. 设定 GDT 表,GDT 通常第一个描述符是空描述符,它的基地址和段界限都为 0。
  2. 加载 GDTR:寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。
  3. 关中断 (cli):保护模式的中断机制与默认实模式不同
  4. 打开地址线A20: 历史遗留问题,这是为了避免“回卷”现象出现
  5. 将 cr0 寄存器的 PE 位置为 1,此时 CPU 就已经进入保护模式
  6. 跳转到 保护模式的代码段 (jmp dword)

描述符构造宏

%macro Descriptor 3dw	%2 & 0FFFFh				; 段界限1dw	%1 & 0FFFFh				; 段基址1db	(%1 >> 16) & 0FFh			; 段基址2dw	((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)	; 属性1 + 段界限2 + 属性2db	(%1 >> 24) & 0FFh			; 段基址3
%endmacro ; 共 8 字节

示例:在 DOS 下进入 x86 保护模式

%include	"pm.inc"	; 常量, 宏, 以及一些说明org	0100hjmp	LABEL_BEGIN[SECTION .gdt]
; GDT
;                              段基址,      段界限     , 属性
LABEL_GDT:	   Descriptor       0,                0, 0           ; 空描述符
LABEL_DESC_CODE32: Descriptor       0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段
LABEL_DESC_VIDEO:  Descriptor 0B8000h,           0ffffh, DA_DRW	     ; 显存首地址
; 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
; END of [SECTION .gdt][SECTION .s16]
[BITS	16]
LABEL_BEGIN:mov	ax, csmov	ds, axmov	es, axmov	ss, axmov	sp, 0100h; 初始化 32 位代码段描述符xor	eax, eaxmov	ax, csshl	eax, 4add	eax, LABEL_SEG_CODE32mov	word [LABEL_DESC_CODE32 + 2], axshr	eax, 16mov	byte [LABEL_DESC_CODE32 + 4], almov	byte [LABEL_DESC_CODE32 + 7], ah; 为加载 GDTR 作准备xor	eax, eaxmov	ax, dsshl	eax, 4add	eax, LABEL_GDT		; eax <- gdt 基地址mov	dword [GdtPtr + 2], eax	; [GdtPtr + 2] <- gdt 基地址; 加载 GDTRlgdt	[GdtPtr]; 关中断cli; 打开地址线A20in	al, 92hor	al, 00000010bout	92h, al; 准备切换到保护模式mov	eax, cr0or	eax, 1mov	cr0, eax; 真正进入保护模式jmp	dword SelectorCode32:0	; 执行这一句会把 SelectorCode32 装入 cs,; 并跳转到 Code32Selector:0  处
; END of [SECTION .s16][SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS	32]LABEL_SEG_CODE32:mov	ax, SelectorVideomov	gs, ax			; 视频段选择子(目的)mov	edi, (80 * 11 + 79) * 2	; 屏幕第 11 行, 第 79 列。mov	ah, 0Ch			; 0000: 黑底    1100: 红字mov	al, 'P'mov	[gs:edi], ax; 到此停止jmp	$SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]

从 x86 保护模式返回实模式

[SECTION .gdt]
; GDT
;
LABEL_DESC_NORMAL: Descriptor    0,         0ffffh, DA_DRW    ; Normal 描述符
; ....[SECTION .s16]
[BITS	16]
LABEL_BEGIN:mov	ax, csmov	ds, axmov	es, axmov	ss, axmov	sp, 0100hmov	[LABEL_GO_BACK_TO_REAL+3], axmov	[SPValueInRealMode], sp
;....LABEL_REAL_ENTRY:		; 从保护模式跳回到实模式就到了这里mov	ax, csmov	ds, axmov	es, axmov	ss, axmov	sp, [SPValueInRealMode]in	al, 92h		; `.and	al, 11111101b	;  | 关闭 A20 地址线out	92h, al		; /sti			; 开中断mov	ax, 4c00h	; `.int	21h		; /  回到 DOS
; END of [SECTION .s16]; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN	32
[BITS	16]
LABEL_SEG_CODE16:; 跳回实模式:mov	ax, SelectorNormalmov	ds, axmov	es, axmov	fs, axmov	gs, axmov	ss, axmov	eax, cr0and	al, 11111110bmov	cr0, eaxLABEL_GO_BACK_TO_REAL:jmp	0:LABEL_REAL_ENTRY	; 段地址会在程序开始处被设置成正确的值Code16Len	equ	$ - LABEL_SEG_CODE16; END of [SECTION .s16code]

指令扩展

在 实模式,保护模式下的部分指令 mul, div, push, pop 等行为有明显差异,例如 push

  • 如果操作数为 8 位,实模式会扩展到 16 位,保护模式 (32 位) 户扩展到 32 位压入栈
  • 如果操作数为 16 位或32位,实模式和保护模式都直接压入 sp - 2 / sp - 4

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/66842.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

CSP2024-30

A 题意:将一个圆等分为 \(K\) 分,给出其中 \(n\) 个等分点的编号,\(x_i < x_{i + 1}\)。 有向边 \(i \to j\) 存在,当且仅当 \(j\) 是距离 \(i\) 最大的点(不唯一),且与图中其他边无交点(端点不算)。 求图中最多有多少条边。\(3 \le K \le 10^9, 3 \le n \le \min(…

小白上手Arcgis—用于结合Netlogo、matlab等进行复杂网络操作

小白上手Arcgis(Netlogo复杂网络数据预处理) 1.前言废话:昨天突然想到可以写一下博客,用来记录一下自己的工作,主要是涉及复杂网络方面。情况简介:本人Arcgis小白,之前只是略微知道有这么个软件,以及知道怎么打开软件。学渣一个,而且不是学gis方向的,但由于工作需要,要…

windows10如何安装jdk8,并且配置java home环境?超详细!

前言 大家好,我是小徐啊。记得我刚学习Java的时候,我的老师第一步就是教我们如何安装jdk并且配置java环境。这应该算是学习Java的第一步吧。虽然这个安装过程对我来说已经不是非常难了,但是我知道,对于一些刚入门的小伙伴还是经常容易搞错的,所以,今天小徐就写一篇详细的…

安装小雅问题

如何卸载重装小雅、apt remove xiaoya docker stop 01ec8396b2c529819bb7c95091a88a9af6999c042bcb7ab57662837c97dca5cd docker rm 01ec8396b2c529819bb7c95091a88a9af6999c042bcb7ab57662837c97dca5cdsystemctl start cpolar 开启cplpr systemctl status cpolar

leetcode24 两两交换链表中的节点(swap-nodes-in-pairs)

### 题目描述: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 示例 1:输入:head = [1,2,3,4] 输出:[2,1,4,3]示例 2: 输入:head = [] 输出:[]示例 3: 输入:head = [1] 输出…

第一章:Borel测度

第1章 Borel测度 在正式讨论我们的内容之前我们先做几点说明 1.我们只讨论\(\mathbb{R}^n\) 上的测度,因此如果不作特别说明,我们均认为测度和集合为于\(\mathbb{R}^n\) 中: 2.我们不特别区分外测度和测度,因为将外测度限制在可测集上就是可测集上的测度: 3.我们默认读者已…

TypeScript在vue中的使用-----事件类型的获取

当我们要对事件定义类型。一种是通过console.log(e)来看事件的类型。另外一种是@事件名的时候,将$event写好,鼠标放上去看事件类型。再讲$event删除。 如下: 然后我们定义函数的时候就可以指定事件类型了const clickMi = (e:MouseEvent)=>{console.log(e.pageX, e.pageY…

信息学奥赛复赛复习08-CSP-J2020-03表达式前置知识点-后缀表达式、栈、字符读取

PDF文档公众号回复关键字:202410011 P1449 后缀表达式 [题目描述] 所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级) 本题中运算符仅包含 + - * / 。保证…