手写操作系统(5)——CPU工作模式与虚拟地址(上)

简介: 手写操作系统(5)——CPU工作模式与虚拟地址

CPU工作模式

按照CPU功能升级迭代的顺序,CPU的一共有三种工作模式:实模式、保护模式、长模式

在不同的工作模式下,CPU执行程序的方式不同,至于有什么不同以及为什么会造成不同,接着往下看。

实模式(Real Mode)

实模式,何为实?

一是指运行的指令是真实的,没有权限区分

二是指运行的地址是真实的,与内存地址一一对应,并且可以操控任意位置的内存

内存寻址

在理解实模式运行过程之前,先来看看在X86 CPU实模式下的寄存器有哪些:

上面这些寄存器有什么用?

当CPU运行某段指令时,需要先将指令从存储介质,如内存、硬盘中取出放到对应的寄存器中,CPU才可以执行这段指令。而取出指令或指令所需的数据时,也需要寄存器来确定内存地址,一般是**段基地址+偏移地址,**以8086CPU(16位寄存器,20位内存寻址)为例,访问内存地址数据如下:

这就是早期的分段式内存管理模式,代码段由CS+IP来确定,栈段由SS+SP来确定。

执行中断

所谓中断即是中断执行当前的指令,转而去执行指定的指令。中断分为硬件中断和软中断

硬件中断是某个设备的中断控制器给CPU发送特定的电信号,CPU对其作出应答,并获取中断控制器发送的中断号

**中断号是什么?**往下看。

软件中断就是CPU执行的指令为**INT 常数。**这个常数也是上文所说的中断号。

中断号可以看作是“excel表格”的行号,当CPU获取到中断号时,就会去加载一个特定“表格”指定行的内容并根据其内容执行相应的指令

上文所说的“excel表格”就是存放在内存中的中断向量表(在BIOS阶段加载到内存的),这个表的地址和长度由特定寄存器IDTR指向,表中一条记录由代码段地址和代码段内偏移地址组成,如下:

在实模式下,中断即是保存CS、IP(当前指令执行到哪了)及相关数据到栈中,然后根据中断号去中断向量表中装载新的内容到CS、IP寄存器中,从而实现中断响应。

保护模式

从前面的介绍了解到,实模式对于指令和地址不加区分,这其实会造成很大的隐患——如果某个程序恶意修改某个内存地址的内容从而造成系统崩溃怎么办

但是凡事皆应该有限度,CPU执行代码也不例外。

保护模式就是对CPU执行指令的权限、内存地址加以限制,从而保证计算机运行稳定。

内存寻址

保护模式下对CPU指令和地址加以限制是通过一些额外的寄存器来实现的,:


**为了区分哪些指令(如 in、out、cli)和哪些资源(如寄存器、I/O 端口、内存地址)**可以被访问,CPU 实现了特权级。CPU特权级共分为4级,R0~R3,其中R0具有最高权限,可以执行所有指令,而后的R123依次递减,如下:

为了实现对内存段的保护,我们需要对各内存段进行区分,我们将其称为段描述符,并放置在内存中,一个段描述符64位,其中包含段基地址、段长度、段权限、类型、可读、可写等,其格式如下:

将多个这样的段描述符组合在一起便有了全局段描述符表GDT该表的基地址和长度(表明该表的起始和界限)由CPU的GDTR寄存器指出,如下图所示。

实模式下进行内存访问时CS、DS、ES等段寄存器中存储的是段基地址,但是在保护模式下,进行内存访问时需要通过一个段选择子(Selector)来决定是否有权利访问及具体的访问位置。

段选择子主要有三个部分:**段描述符索引、TI、请求特权级,**如下图所示(暂不考虑前面64位的影子寄存器):


根据段描述符索引索引及GDT表中的全局描述符表基址就可以找到该内存的段描述符

PS:这里的设计很好的节约了空间,由于每个段描述符是64位8字节,因此每个索引之间的差距就是8字节(1000 2000 3000``````),因此低三位可以用作他途(不用的话总为0,浪费了),存储TI和RPL。在实际计算的时候对索引左移三位就可以了。

TI表示该内存段描述符是在GDT中还是在LDT中,LDT可以看成是GDT的子集,是某个任务所需内存段描述符的集合表。LDT所在内存段地址也是通过GDT中的段选择子找到的,如果说GDT是一级查询表,LDT就是二级查询表。详情可见:LDT详解

RPL表示请求访问内存的执行程序的当前权限级别(CPL),CPL是由CS和SS的RPL组成的,通常RPL=CPL。而后只有当RPL<=段描述符中的DPL时,才能够访问

PS:特权级越低,特权越大(R0)。

影子寄存器又称为段描述符高速缓冲寄存器,是为了避免每次都访问GDT而采用硬件提速的方式来缓存对应的段描述符。

平坦模型与分页

内存分段模型相较于内存分页模型有很多缺陷(使用率、置换等),因此现代操作系统常用的是内存分页模型。不过X86 CPU需要在分段的前提下根据需要进行分页,而平坦模型则是通过让分段“虚假”,从而达到必定分页的目的:

可知,32位的寄存器最高寻址空间为4GB,因此一个段长度最大也只能为4GB,如果将所有段的基地址设为0,长度设为0xfffff,段长度的粒度设为4KB,则所有段都指向同一个地址空间:0~4GB-1(1M个4K大小的地址空间)。

执行中断

同内存访问一样,在保护模式下执行中断也需要进行权限判断,因此需要像段描述符一样扩展中断向量表中的信息,将扩展后的中断向量中的信息称为中断描述符(中断门),其格式如下:

将中断描述符组合放在内存中,即是中断门描述符表IDT,加载在相应的寄存器IDTR中,通过中断号来获取相应的中断门描述符流程如下:


产生中断时,CPU根据中断号去获取相应的中断门描述符,会进行如下判断:

  • 中断号是否越界(如X86最大是256)
  • 中断/陷阱门(中断门进入中断自动将IF设为0,从而防止嵌套中断//可以人为打开//,而陷阱门不会,因此陷阱门适合处理异常)
  • 1表示在内存中
  • 进行权限检查,这一步骤详细过程如下:

CPL<=中断门描述符的DPL(必须有足够高的权限才能进行中断),进入下一步;否则抛出一个保护异常;

CPL>=描述符中段选择子的DPL(特权级不能过高,不然还需要中断做什么,而且禁止进行高特权向低特权的转换),进入下一步;否则抛出一个保护异常;

如果CPL=段选择子指向的段描述符中的DPL,则表示同级权限,不用进行栈切换;否则,需要进行栈切换,需要从TSS中加载具体权限的各寄存器值。有关上述权限切换,可以参考:Linux权限切换

PS:TR是任务寄存器,用于寻址任务状态段(Task State Segment,TSS),TSS存储当前执行任务的相关信息,也是64位8字节

TR寄存器用于存放当前任务TSS段的16位段选择符、32位基地址、16位段长度和描述符属性值它引用GDT表中的一个TSS类型的描述符。当执行任务切换时,处理器会把新任务的TSS的段选择符和段描述符自动加载进任务寄存器TR中

在进行完上述流程之后,CPU才会加载目标中断代码进行中断服务程序的执行。

开启保护模式

X86 CPU加电后自动进入实模式,那么如何切换到保护模式呢?

  • 准备全局段描述符GDT
GDT_START: 
//CPU硬件要求第一个段描述符为0
knull_dsc: dq 0 
//dq表示4个字,8字节
kcode_dsc: dq 0x00cf9e000000ffff 
kdata_dsc: dq 0x00cf92000000ffff 
GDT_END: 
GDT_PTR: 
GDTLEN  dw GDT_END-GDT_START-1 
GDTBASE  dd GDT_START 
  • 让GDTR指向GDT
lgdt [GDT_PTR]
  • 设置CR0,开启保护模式
;开启 PE 
mov eax, cr0 
bts eax, 0; 将CR0.PE =1,即打开保护模式
mov cr0, eax    
  • 长跳转,刷新CS段寄存器
jmp dword 0x8:_32bits_mode ;//_32bits_mode为32位代码标号即段偏移

(***)上述0x8:32位_段偏移,是一个长跳转,发现CR0的值为1,因此CPU处于保护模式,这时0x8即为上文中的段选择子:0000 0000 0000 1000,从GDTR中根据索引号8,去获取相应的段描述符,在加上32位_段偏移,最终实现了一次内存访问!

PS:虽然我们想让CS段寄存器更新,CS段寄存器无法通过mov等直接赋值的操作进行赋值,指令集没有设计这样的功能,只能通过跳转等方法来改变它

PS:汇编中jmp是跳转指令,


jmp short 标号 段间跳转 -128-127


jmp far ptr 标号 超段转移 跳转包含目标地址


jmp reg 16位寄存器


jmp word ptr 内存单元地址 段内转移


jmp dword ptr 内存单元地址 ( 段间间接转移) 高字地址存放cs 低字节存放ip。


jmp 1000H:2000H 段间直接转移,段码和偏移量直接给出

小结

我们来看看在保护模式下进行一次段偏移的流程是如何进行的:


手写操作系统(5)——CPU工作模式与虚拟地址(下):https://developer.aliyun.com/article/1508587

相关文章
|
2月前
|
机器学习/深度学习 Dart 前端开发
移动应用与系统:构建现代数字生态的基石在当今这个高度数字化的社会中,移动应用与操作系统已成为我们日常生活不可或缺的一部分。它们不仅改变了我们的沟通方式,还重塑了我们的工作、学习和娱乐模式。本文将深入探讨移动应用开发的基础、移动操作系统的功能以及这两者如何共同塑造了我们的数字世界。
随着智能手机和平板电脑的普及,移动应用与系统的重要性日益凸显。它们不仅为用户提供了便捷的服务和丰富的功能,还为开发者提供了广阔的创新平台。本文将介绍移动应用开发的基本概念、技术栈以及最佳实践,并探讨主流移动操作系统的特点和发展趋势。通过分析移动应用与系统的相互作用,我们可以更好地理解它们在现代社会中的重要地位。
|
1月前
|
存储 Java C语言
MacOS环境-手写操作系统-04-实模式进入保护模式
MacOS环境-手写操作系统-04-实模式进入保护模式
15 1
|
2月前
|
安全
探索操作系统的心脏:内核与用户模式的交互之旅
【9月更文挑战第12天】在数字世界的海洋中,操作系统扮演着灯塔的角色,指引着每一条数据流的方向。本文将深入探讨操作系统的核心机制——内核与用户模式,揭示它们如何协同工作以保障计算机系统的高效与安全。我们将从基础概念出发,逐步深入到实际代码示例,旨在为读者呈现一幅清晰的操作系统工作原理图景。
|
1月前
|
存储 iOS开发 C++
MacOS环境-手写操作系统-05-保护模式超强寻址
MacOS环境-手写操作系统-05-保护模式超强寻址
24 0
|
2月前
|
安全
探索操作系统的心脏:内核与用户模式的奥秘
在数字世界的海洋中,操作系统如同一艘巨轮,承载着无数数据的流动。本文将揭开这艘巨轮的核心机密——内核与用户模式,带你领略它们如何协同工作,确保系统的稳定与安全。通过浅显易懂的语言和生动的比喻,我们将一探究竟,看看这两种模式如何在幕后默默支撑着我们的日常计算体验。准备好了吗?让我们启航,深入操作系统的心脏地带!
【原创】报告CPU当前工作模式的汇编源代码
【原创】报告CPU当前工作模式的汇编源代码
|
3月前
|
安全 调度 开发者
探索操作系统的心脏:内核与用户模式
【8月更文挑战第30天】在数字世界的每一次跳动中,操作系统扮演着至关重要的角色。本文将深入浅出地探讨操作系统的核心概念——内核与用户模式,通过生动的比喻和直观的解释,带领读者理解这一复杂主题。我们将从内核的定义和功能出发,逐步揭示用户模式的秘密,并通过代码示例,展示如何在实际应用中区分和利用这两种模式。无论你是计算机科学的初学者还是资深开发者,这篇文章都将为你打开一扇了解操作系统深层工作原理的大门。
|
3月前
|
安全 Linux Windows
探索操作系统的心脏:内核与用户模式的奥秘
本文将深入探讨操作系统的核心组件——内核,以及它如何通过用户模式和内核模式的切换来管理资源和保护系统安全。我们将从基础概念出发,逐步深入到内核的设计原理,最后讨论现代操作系统中用户模式与内核模式的交互机制。通过这篇文章,读者将获得对操作系统内核工作原理的深刻理解,并认识到它在计算机系统中的重要性。
|
4月前
|
Linux 调度
部署02-我们一般接触的是Mos和Wimdows这两款操作系统,很少接触到Linux,操作系统的概述,硬件是由计算机系统中由电子和机械,光电元件所组成的,CPU,内存,硬盘,软件是用户与计算机接口之间
部署02-我们一般接触的是Mos和Wimdows这两款操作系统,很少接触到Linux,操作系统的概述,硬件是由计算机系统中由电子和机械,光电元件所组成的,CPU,内存,硬盘,软件是用户与计算机接口之间
|
5月前
|
虚拟化 iOS开发 MacOS
客户机操作系统已禁用 CPU。请关闭或重置虚拟机。解决方法
客户机操作系统已禁用 CPU。请关闭或重置虚拟机。解决方法
2700 0