7.4.1 Intel CPU物理结构

https://www.cnblogs.com/megachen/p/9768115.html

x86实模式

实模式

  • 20位:1M内存空间
  • 地址表示方式:段地址(16位):偏移地址(16位)
  • 段地址4位对齐

保护模式(Protect Mode)

  • 32位地址空间:4G内存
  • 支持多任务、任务切换、上下文保护
  • 进程隔离:代码和数据的安全
  • 支持分段机制和分页机制
  • 新的寄存器
    • EAX~EDX:扩充到32位
    • CR0~CR4
    • GDTR
    • LDTR
    • IDTR
    • ……

保护模式的寄存器模型

控制寄存器CR0

CR0的低5位组成机器状态字(MSW)

  • PE:0—实模式;1—保护模式
  • MP:1(系统有数字协处理器时)
  • EM:0(仿真协处理器)
  • TS:任务切换,切换任务时自动设置
  • PG:允许分页

控制寄存器CR2

如果发生缺页,引发缺页的线性地址保存在CR2中

控制寄存器CR3

CR3包含页目录基址:高20位

x86 CPU架构下的三种地址

逻辑地址:汇编语言(段:偏移)

若在保护模式下,DS不能理解为段基址

线性地址:由逻辑地址转换得到

物理地址

未分页 线性地址==物理地址

分页 线性地址!=物理地址

x86 CPU架构下的三种地址

线性地址(Linear Address)是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。

第一级:段机制(逻辑地址到线性地址)

第二级:分页机制(线性地址到物理地址)

逻辑地址、物理地址、线性地址

Intel架构下

凡是在代码中书写的内存地址都是逻辑地址,也就是采用基地址+偏移量,我们能够直接看到的也是逻辑地址,通过左移等操作可以计算出对应的物理地址(在实模式下逻辑地址与物理地址没有中间层所以是可以的,但是在保护模式则是行不通的,再说)

实模式下

逻辑地址通过左移等操作可以计算出对应的物理地址

保护模式

  • 在逻辑地址和物理地址之间多了一个中间层线性地址
  • 逻辑地址左移等操作计算出来的是线性地址而不再是物理地址了, 线性地址就是32位的整数
  • 通过分页机制将线性地址转为32位的物理地址

7.4.2 Intel CPU段机制

段与段描述符

一段连续内存

段描述符

描述段的属性,8字节(保护模式)

  1. 段基址
  2. 段界限
  3. 段属性
  4. 段类型
  5. 访问该段所需最小特权级
  6. 是否在内存
  7. ……

描述符(Descriptor)

段基址:32位(段基址1+段基址2)

段界限:20位(段界限1+段界限2)

TYPE值 数据段和代码段描述符 S=1 系统段和门描述符 S=0
0 只读 <未定义>
1 只读,已访问 可用286TSS
2 读/写 LDT
3 读/写,已访问 忙的286TSS
4 只读,向下扩展 286调用门
5 只读,向下扩展,已访问 任务门
6 读/写、向下扩展 286中断门
7 读/写,向下扩展,已访问 286陷阱门
8 只执行 <未定义>
9 只执行、已访问 可用386TSS
A 执行/读 <未定义>
B 执行/读、已访问 386TSS
C 只执行、一致码段 386调用门
D 只执行、一致码段、已访间 <未定义>
E 执行/读、一致码段 386中断门
F 执行/渎、一致码段、已访问 386陷阱门

描述符的数据结构

typedef struct Descriptor  {
unsigned int base24_31; //:8 基地址的高8位
unsigned int g; //:1 段长单位,0:字节
unsigned int d_b; //:1
unsigned int unused; //:1
unsigned int avl; //:1
unsigned int seg_limit_16_19; //:4 段界限高4位
unsigned int p; //:1
unsigned int dpl; //:1
unsigned int s; //:1
unsigned int type; //:4
unsigned int base_0_23; //:24 基地址的低24位
unsigned int seg_limit_O_15; //:16 段界限低16位
}

描述符表(Descriptor Table)

描述符表

存放描述符的数组

长度:8字节的整数倍

描述符表类型

  • 全局描述符表GDT:Global Descriptor Table
  • 局部描述符表LDT:Local Descriptor Table
  • 中断描述符表IDT:Interrupt Descriptor Table

全局描述符表GDT:Global Descriptor Table

包含所有进程可用的段描述符,系统进1个GDT表

局部描述符表LDT:Local Descriptor Table

包含与特定进程有关的描述符,每个进程由自己的LDT

中断描述符表IDT:Interrupt Descriptor Table

包含终端服务程序段的描述符(中断门描述符)

类似中断向量表

选择子(Selector)

选择子用于选择GDT/LDT中的某个描述符

  • 存放在段寄存器中:高13位是整数索引。

构成

  • 索引域(INDEX):13位,给出段描述符在GDT或者IDT中的位置
  • TI域(Table Indicator):1位,GDT(0)或IDT(1)
  • 特权级别域(Request Privilege Level):2位

例:LDT基址0012 0000H,GDT基址00100000H,CS=1007H

  1. 请求的特权级是多少?
  2. 目标段的描述符位于GDT中还是LDT中?
  3. 目标段的描述符的基地址是多少?

解:(CS)=1007H=0001 0000 0000 0111B

  1. RPL=3,申请的特权级位3

  2. TI=1,描述符位于LDT内

  3. 描述符相对于LDT基址的偏移量位

    \(0001 0000 0000 0B \times 8=512 \times 8 = 4096 = 1000H\)

    段描述符的地址为

    \(0012 0000H + 1000H = 0012 1000H\)

把逻辑地址转换到线性地址(32位,4G)

7.4.3 Linux页面机制

硬件分页

分页

  • Intel CPU的页
  • 通过设置CR0的PG位开启分页功能
  • 分页:线性地址→物理地址(线性地址是分段功能获取的)
  • 在MMU中进行分页

Linux的三级页表结构

普通页表实现时的问题

32位OS(4G空间),每页4K,页表每个记录占4字节

  • 进程的页数:4G/4K=1M个页

    • 页表的记录应有1M条记录

      • 页表所占内存:1M*4字节=4M

        • 页表占页框数:4M/4K=1K页框(连续)

问题:

  1. 难以找到连续1K个页框存放页表
  2. 页表全部放入过度消耗内存(4M)

解决办法

  1. 将4M的超大页表存储到离散的1K个页框中
  2. 仅将页表的部分内容调入内存
页号 页框号 中断位 外存地址 访问位 修改位
0 8 0 4000 1 0
1 24 0 8000 1 1

二级页表

把超大的页表(4M)以页为单位分成若干个小页表,存入离散的若干个页框中

为了对小页表进行管理和查找,另设置一个叫页目录的表,记录每个小页表的存放位置(页框号)

  • 页目录实际上是一个特殊页表:每个记录存放的是小页表的标号和其所在的页框号之间的对应关系

页目录:一级页表或外部页表;小页表:二级页表

Windows NT二级页表的结构

页目录号:小页表页号(页目录的索引)31-22

页号:页面的编号(页表的索引)21-12

页偏移:页偏移 11-0

二级页表地址的映射特点

  1. 访问数据需要三次访问内存
  2. 页目录调入内存
  3. 页表按需要调入内存
  4. 页面、页表、页目录的大小都刚好4K(占一个页框)

7.4.4 Linux对段的支持

Linux段机制

进程建立时,段机制对寄存器初始化:start_thread()

#define start_thread(regs, new_eip, new_esp) do {
__asm__("movl %0,%%fs ; movl %0,%%gs": :"r" (0));
set_fs(USER_DS);
//对段寄存器初始化
//__USER_DS数据段
regs->xds = __USER_DS;
regs->xes = __USER_DS;
regs->xss = __USER_DS;
//__USER_CS代码段
regs->xcs = __USER_CS;
regs->eip = new_eip;
regs->esp = new_esp;
} while (0)
#define __KERNEL_CS   0X10
#define __KERNEL_DS 0x18
#define __USER_CS 0x23
#define __USER_DS 0x2B
INDEX TI DPL
__KERNEL_CS 0X10 0000 0000 0001 0 0 0 0
__KERNEL_DS 0x18 0000 0000 0001 1 0 0 0
__USER_CS 0x23 0000 0000 0010 0 0 1 1
__USER_DS 0x2B 0000 0000 0010 1 0 1 1

INDEX:2,3,4,5;TI=0;DPL:0、3

GDT定义

ENTRY(gdt_table)
.quad 0x0000000000000000 /*NULL descriptor*/
.quad 0x0000000000000000 /*not used*/
.quad 0x00cf9a000000ffff /*0x10 kernel 4GB code at 0x00000000*/
.quad 0x00cf92000000ffff /*0x18 kernel 4GB data at 0x00000000*/
.quad 0x00cffa000000ffff /*0x23 user 4GB code at 0x00000000*/
.quad 0x00cff2000000ffff /*0x2b user 4GB data at 0x00000000*/
.quad 0x0000000000000000 /*not used*/
.quad 0x0000000000000000 /*not used*/

xxxx xxxx Gl00 hhhhPDP0 1010 xxxx xxxx xxxx xxxx xxxx xxxx hhhh hhhh hhhh hhhh

K_CS:0000 0000 1100 111110011010 0000 0000 0000 0000 0000 0000 1111111111111111

K_DS:0000 0000 1100 11111001 0010 0000 0000 0000 0000 0000 0000 1111111111111111

U_CS:0000 0000 1100 111111111010 0000 0000 0000 0000 0000 0000 1111111111111111

U_DS:0000 0000 1100 11111111 0010 0000 0000 0000 0000 0000 0000 1111111111111111 .

  • XXXX:基地址;hhhh:段界限
  • G位倒是1(段长单位4KB);P位都是1(段在内存)

Linux的段机制

  1. Linux四个范围一样的段:0~0xFFFFFFFF(4G)

    • 内核数据段|内核代码段|用户数据段|用户代码段
  2. 各段属性不同
    • 内核段特权级为0
    • 用户段特权级位3
  3. 作用
    • 利用段机制隔离用户数据和系统数据

      • 保留段的等级保护机制
    • 简化(避免)逻辑地址到线性地址转换
      • 可以直接将虚拟地址当作线性地址,二者完全一致

最新文章

  1. JDBC查询数据库中的数据
  2. 《PHP开发APP接口》笔记
  3. ASCII值对照表
  4. poj 2823 Sliding Window (单调队列入门)
  5. 坑爹的VS2012
  6. .NET设计模式(15):结构型模式专题总结(转)
  7. JAVA多线程学习2--线程同步
  8. Hibernate逍遥游记-第7章 Hibernate的检索策略和检索方式(&lt;set lazy=&quot;false&quot; fetch=&quot;join&quot;&gt;、left join fetch、FetchMode.JOIN、)
  9. 实验一:基于Winsock完成简单的网络程序开发
  10. UESTC_邱老师的脑残粉 2015 UESTC Training for Graph Theory&lt;Problem D&gt;
  11. Hive 伪分布式下安装
  12. OVS 总体架构、源码结构及数据流程全面解析
  13. C和C指针小记(十七)-使用结构和指针-链表
  14. pyculiarity 时间序列(异常流量)异常检测初探——感觉还可以,和Facebook的fbprophet本质上一样
  15. jQuery.cookie的使用指南
  16. 【译】GNU Radio How to write a block 【如何开发用户模块及编写功能块】
  17. js的函数作用域跟块级作用域
  18. Ubuntu安装新版本nodejs的5种姿势
  19. 解决TextBox Ctrl+A不能全选的问题
  20. Android逆向之旅---静态方式分析破解视频编辑应用「Vue」水印问题

热门文章

  1. python 并发专题(四):yield以及 yield from
  2. Python面向对象02/类的空间问题、类与对象之间的关系、类与类之间的关系
  3. git和github连接权限(这是一个简便方法,不是很安全,建议大家还是用ssh解决)
  4. 如何在项目中封装api
  5. java的干儿子锁Lock
  6. 数据结构C语言实现----出队伍操作
  7. Jenkins链接Kubernetes集群
  8. Mybatis——Mapper代理
  9. pycharm控制台输出的日志全是红色的字体?
  10. 哇咔咔干货来啦:PowerJob 原理剖析之 Akka Toolkit