从8086的实模式到80386的保护模式的地址映射发展

一、x86体系结构的发展历史:

1.常见处理器:

  • inter:pc主流的处理器;
  • AMD:部分pc电脑;
  • arm:主要用于手机和平板等;
  • ppc,mips:主要用于一些智能眼镜、只能手表等智能可穿戴设备。

2.CPU位数含义:

CPU的位数代表着CPU的计算能力,即就是ALU的计算能力,即ALU一次能够计算的最大宽度,最长的字节长度,也就是做加法。
CPU位数 == ALU的宽度,ALU需要从寄存器取数据,寄存器数据来自数据总线,也就是:
CPU位数 == ALU的宽度 == 数据总线的条数。

地址总线的条数代表:可以访问的地址范围,假设地址总线16条,可以访问的最大地址为2^16

3.x86体系开创过程:

  • x86体系始于8086,实际上真正意义上的32位的x86体系从80386开始的,80186、80286都是过渡阶段;

二、8086地址映射简介:实模式

1.访问过程:

  • 地址总线:20条(可访问最大地址为2^20,即1M)
  • 数据总线:16条(总线上数据最大能传递2字节宽度的数据)

我们发现,数据总线中无法存放20位的地址,因为数据总线最大宽度为16位,此时引入4个16位的寄存器:IP寄存器存放地址的偏移量

  • CS:code section 指令寄存器
  • DS:data section 数据寄存器
  • SS:stack section 堆栈寄存器
  • ES:expand section 扩展寄存器

8086将物理内存划分为不同的段,正如寄存器名一样,指令段、数据段、堆栈段、扩展段等。
此时,规定每个内存段的起始地址为16的倍数,[16,2^16],然后将每个段的起始地址保存在对应的寄存器中,但是由于寄存器是16位的,地址都是20位的。

num % 16 == 0
一个数字可以被16整除,二进制的低四位全部为0,只需要将内存段的地址的高16位保存在寄存器中
即DS>>4,去掉低4位

所以,最终寄存器中保存的都是不是真正的段起始地址,而是段起始地址的高16位。

假设访问数据段的一个变量,过程如下

  1. 访问DS寄存器,获得数据段的起始地址;
  2. 将寄存器中获得值左移4位,DS<<4,恢复成20位的地址;
  3. 最后,加上该数据在内存段上的偏移量IP
数据的最终地址: DS<<4 + IP
其中,IP又被称为偏移量、偏移地址、逻辑地址

这里的分段是指对物理内存的分割,与段页式管理没联系,段页式管理式在有操作系统的情况下,而这里并没有操作系统。

2.8086存在的缺点:

  1. 没有操作系统的存在,直接访问的是物理内存;
  2. 对内存没有进行权限控制,可以任意的对某一块内存进行修改;
  3. 没有对ip进行安全检查,如果ip特别大,产生越界访问等不好的后果。

直接对物理内存进行操作,没有操作系统保护,此状态被叫做实模式,也叫实地址模式。

3.linux内核image加载:

  • CPU刚通电,操作系统还没有运行起来,CPU强制进入实模式,也就是8086运行的模式,也就是此时的CPU最多可以访问1M的内存;
  • 这也就是说为什么内核从1M以后开始加载,0x100000,1M内存放的式bois的缓存,显卡的缓存,驱动代码等内容。因为1M前放的内容式必须要先访问或者执行的。

三、80386的地址映射:保护模式

1.80386的新突破:

从8086得出一些经验,8086是极其不安全不可靠的,原因如下:

  • 没有内存段的大小说明;
  • 没有内存访问权限的控制;
  • 内存的起始地址也可能被修改。

所以,后来,80386改善和解决8086存在的弊端,新增两个32位寄存器:

  1. GDTR:全局段描述符表,保存全局段描述符表GDT的地址,常驻内存,进程共享
  2. LDTR:局部段描述符表,进程独享,新版本中淘汰

GDT中的内容:GDT就是一个数组,保存内存段的信息
在这里插入图片描述
GDT数组的索引存放在哪里:

从80386开始,CS DS SS ES用来存放段描述表的索引

此时这四个段寄存器的16位的含义不再是保存段起始地址,而是用来描述GDT数组的信息,如下
在这里插入图片描述

  • 权限:00代表最高权限,也就是内核态,11代表最低权限,也就是用户态;
  • 8192:2^13 = 8k;也就是有这么多的索引,即GDT[2^32-1],实际上,linux内核也会占用一部分,大概是12个左右被内核保留使用,实际上用户可以使用的是8180个。

GDT数组的某一项的含义: GDT[n],每一项元素大小为8字节,64位

在这里插入图片描述

  • B:base addr 起始地址,总共32位
  • L:length 段内存长度,总共16位,2^16 = 1M,单位G表示
  • G:表示段内存长度的单位:0表示以字节位单位,1表示段内存长度单位是页面
  • D、A、P、DPL、S:权限控制

2.保护模式下内存分段的地址映射:
在这里插入图片描述

GDT[DS>>3].baseaddr + IP(逻辑地址,需要和length比较一下,防止越界) = 线性地址
当基地址GDT[DS>>3].baseaddr ==0时,IP == 逻辑地址 == 线性地址

DS>>3:获取高13位,也就是GDT数组的索引
GDT[DS>>3]:GDT数组的某一项
  • 获取到线性地址以后,检查是否开启分页机制,如果没有开启,即线性地址 == 物理地址;
  • 开启了分页机制,此时线性地址 == 虚拟地址,要进行分页地址映射,才能得到物理地址。

3.补充提升:

  • CR0:最高位,即PG位,0代表未开启内存分页,1代表开启内存分页
  • CR2:发生缺页异常的虚拟地址
  • CR3:页目录的起始地址
  • CR4:PAE位,物理地址扩展,0表示位开启,1表示开启

总线有外总线和内总线之分,我们程序中打印的地址是内总线发出的地址,也就是虚拟地址,即没有经过页表映射,没有经过转换的地址;
只有当虚拟地址经过了页表映射等过程,才会被放到外总线上,而外总线是直接连接内存的总线。

发布了237 篇原创文章 · 获赞 98 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/KingOfMyHeart/article/details/101224813