【av68676164(p55-p58)】 Intel CPU和Linux内存管理
7.4.1 Intel CPU物理结构
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字节(保护模式)
- 段基址
- 段界限
- 段属性
- 段类型
- 访问该段所需最小特权级
- 是否在内存
- ……
描述符(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
- 请求的特权级是多少?
- 目标段的描述符位于GDT中还是LDT中?
- 目标段的描述符的基地址是多少?
解:(CS)=1007H=0001 0000 0000 0111B
RPL=3,申请的特权级位3
TI=1,描述符位于LDT内
描述符相对于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页框(连续)
- 页表所占内存:1M*4字节=4M
- 页表的记录应有1M条记录
问题:
- 难以找到连续1K个页框存放页表
- 页表全部放入过度消耗内存(4M)
解决办法
- 将4M的超大页表存储到离散的1K个页框中
- 仅将页表的部分内容调入内存
页号 | 页框号 | 中断位 | 外存地址 | 访问位 | 修改位 |
---|---|---|---|---|---|
0 | 8 | 0 | 4000 | 1 | 0 |
1 | 24 | 0 | 8000 | 1 | 1 |
… | … | … | … | … | … |
二级页表
把超大的页表(4M)以页为单位分成若干个小页表,存入离散的若干个页框中
为了对小页表进行管理和查找,另设置一个叫页目录的表,记录每个小页表的存放位置(页框号)
- 页目录实际上是一个特殊页表:每个记录存放的是小页表的标号和其所在的页框号之间的对应关系
页目录:一级页表或外部页表;小页表:二级页表
Windows NT二级页表的结构
页目录号:小页表页号(页目录的索引)31-22
页号:页面的编号(页表的索引)21-12
页偏移:页偏移 11-0
二级页表地址的映射特点
- 访问数据需要三次访问内存
- 页目录调入内存
- 页表按需要调入内存
- 页面、页表、页目录的大小都刚好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的段机制
- Linux四个范围一样的段:0~0xFFFFFFFF(4G)
- 内核数据段|内核代码段|用户数据段|用户代码段
- 各段属性不同
- 内核段特权级为0
- 用户段特权级位3
- 作用
- 利用段机制隔离用户数据和系统数据
- 保留段的等级保护机制
- 简化(避免)逻辑地址到线性地址转换
- 可以直接将虚拟地址当作线性地址,二者完全一致
- 利用段机制隔离用户数据和系统数据
【av68676164(p55-p58)】 Intel CPU和Linux内存管理的更多相关文章
- linux内存管理
一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分: 1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程 ...
- Linux内存管理(二)
Linux内存管理之二:Linux在X86上的虚拟内存管理 本文档来自网络,并稍有改动. 前言 Linux支持很多硬件运行平台,常用的有:Intel X86,Alpha,Sparc等.对于不能够通用的 ...
- Linux内存管理(一)
Linux内存管理之一:基本概念篇 物理地址.线性地址(虚拟地址)和逻辑地址:阐述段式管理和页式管理基本概念:Linux操作系统内存管理和虚拟内存概念:为内核开发做一个基础铺垫. 内存是linux内核 ...
- [转帖]Linux分页机制之概述--Linux内存管理(六)
Linux分页机制之概述--Linux内存管理(六) 2016年09月01日 19:46:08 JeanCheng 阅读数:5491 标签: linuxkernel内存管理分页架构更多 个人分类: ┈ ...
- linux内存管理---物理地址、线性地址、虚拟地址、逻辑地址之间的转换
linux内存管理---虚拟地址.逻辑地址.线性地址.物理地址的区别(一) 这篇文章中介绍了四个名词的概念,下面针对四个地址的转换进行分析 CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步 ...
- linux内存管理---虚拟地址、逻辑地址、线性地址、物理地址的区别(一)
分析linux内存管理机制,离不了上述几个概念,在介绍上述几个概念之前,先从<深入理解linux内核>这本书中摘抄几段关于上述名词的解释: 一.<深入理解linux内核>的解释 ...
- 转 Linux内存管理原理
Linux内存管理原理 在用户态,内核态逻辑地址专指下文说的线性偏移前的地址Linux内核虚拟3.伙伴算法和slab分配器 16个页面RAM因为最大连续内存大小为16个页面 页面最多16个页面,所以1 ...
- 【转帖】linux内存管理原理深入理解段式页式
linux内存管理原理深入理解段式页式 https://blog.csdn.net/h674174380/article/details/75453750 其实一直没弄明白 linux 到底是 段页式 ...
- Linux内存管理和寻址详解
1.概念 内存管理模式 段式:内存分为了多段,每段都是连续的内存,不同的段对应不用的用途.每个段的大小都不是统一的,会导致内存碎片和内存交换效率低的问题. 页式:内存划分为多个内存页进行管理,如在 L ...
随机推荐
- java IO流 (九) Path、Paths、Files的使用
1.NIO的使用说明:>Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO AP.>NI ...
- 数据可视化之 图表篇(一)Power BI可视化,几张图表认识疫情现状
近期国际疫情愈演愈烈,在这个特殊的时期,一方面仍要照顾好自己.不要为疫情防治添乱,另一方面,也可以利用疫情数据提升自己的数据分析和可视化技能. 下面是我制作的几个可视化图表,分别注释了每个可视化用到 ...
- hihoCoder 1049 后序遍历 最详细的解题报告
题目来源:后序遍历 解题思路:开始时我只知道先通过先序.中序求出二叉树,然后再后序遍历二叉树,这当然也是一种解题思路,但是会做一些无用功,比如:计算二叉树.其实,可以直接通过先序序列和中序序列直接求出 ...
- Angular 懒加载找不到模块问题解决方法
问题: 懒加载无法找到模块 解决办法: 在app-routing.module.ts中引入该模块
- 当我谈 HTTP 时,我谈些什么?
当我们打开网站时也许不会去留意网站前面的HTTP是怎么来的.但是它毫无疑问在网络中有着举足轻重的地位.本文从起源到发展,详说HTTP从1到3的演变. 说在前面 本文不致力于讲完 HTTP 的全部内容, ...
- Ethical Hacking - NETWORK PENETRATION TESTING(6)
Creating a fake access point (honeypot) Fake access points can be handy in many scenarios, one examp ...
- NVIDIA GPU Turing架构简述
NVIDIA GPU Turing架构简述 本文摘抄自Turing官方白皮书:https://www.nvidia.com/content/dam/en-zz/Solutions/design-vis ...
- SpringBoot + Spring Cloud Consul 服务注册和发现
什么是Consul Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置.与其它分布式服务注册与发现的方案,Consul 的方案更"一站式" ...
- web自动化 -- 三种等待方式
一.强制等待 二.隐式等待 注:隐式等待的作用域是全局,所以一般设置在整局代码的头几行. 如: 三.显示等待 元素存在: 元素可见: 元素可点击: 看到上图源码中有一个 element.is_en ...
- 今天发现郭的华为手机无法读写sd卡,找到了这个方法
https://bbs.csdn.net/topics/391985867?page=1 华为P9是android 6.0 的==在API23+以上也就是安卓6.0以上的,进行了权限管理不止要在And ...