一、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位。
假设访问数据段的一个变量,过程如下
- 访问DS寄存器,获得数据段的起始地址;
- 将寄存器中获得值左移4位,DS<<4,恢复成20位的地址;
- 最后,加上该数据在内存段上的偏移量IP
数据的最终地址: DS<<4 + IP
其中,IP又被称为偏移量、偏移地址、逻辑地址
这里的分段是指对物理内存的分割,与段页式管理没联系,段页式管理式在有操作系统的情况下,而这里并没有操作系统。
2.8086存在的缺点:
- 没有操作系统的存在,直接访问的是物理内存;
- 对内存没有进行权限控制,可以任意的对某一块内存进行修改;
- 没有对ip进行安全检查,如果ip特别大,产生越界访问等不好的后果。
直接对物理内存进行操作,没有操作系统保护,此状态被叫做实模式,也叫实地址模式。
3.linux内核image加载:
- CPU刚通电,操作系统还没有运行起来,CPU强制进入实模式,也就是8086运行的模式,也就是此时的CPU最多可以访问1M的内存;
- 这也就是说为什么内核从1M以后开始加载,0x100000,1M内存放的式bois的缓存,显卡的缓存,驱动代码等内容。因为1M前放的内容式必须要先访问或者执行的。
三、80386的地址映射:保护模式
1.80386的新突破:
从8086得出一些经验,8086是极其不安全不可靠的,原因如下:
- 没有内存段的大小说明;
- 没有内存访问权限的控制;
- 内存的起始地址也可能被修改。
所以,后来,80386改善和解决8086存在的弊端,新增两个32位寄存器:
- GDTR:全局段描述符表,保存全局段描述符表GDT的地址,常驻内存,进程共享
- 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表示开启
总线有外总线和内总线之分,我们程序中打印的地址是内总线发出的地址,也就是虚拟地址,即没有经过页表映射,没有经过转换的地址;
只有当虚拟地址经过了页表映射等过程,才会被放到外总线上,而外总线是直接连接内存的总线。