# 内存管理之三
# Intel CPU与Linux内存管理
# Intel CPU物理结构
x86计算机刚刚加电时,处在实模式(Real Mode)下
# 实模式(Real Mode)
- 20位地址总线:1M内存空间
- 地址表示方式:段地址(16位):偏移地址(16位)
- 段地址低4位对齐(段地址低4位永远是0,所以凑出来20位)
- 实模式程序直接存取物理内存
# 保护模式(Protect Mode)
每个程序在自己的一个4G的空间内运行,程序间不会互相影响,保护了内核和进程。
- 32位地址空间:4G内存
- 支持多任务、任务切换、上下文保护
- 进程隔离:代码和数据安全
- 支持分段机制和分页机制
- 新的寄存器:
- EAX~EDX:扩充到32位
- CR0~CR4
- GDTR
- LDTR
- IDTR
# x86 CPU架构下的三种地址
逻辑地址:汇编语言(段:偏移)
三种地址转换流程# Intel CPU段机制
# 段和段描述符
段:一段连续内存
段描述符:8个字节,描述段的属性,段基址、段界限、段属性、段类型、是否存在内存等等
逻辑地址转换为线性地址流程:
# Linux页面机制
# Intel分页
- Intel CPU的页:4KB
- 通过设置寄存器CR0的PG位开启分页功能
- 分页:线性地址->物理地址
- 在MMU(管理分页的硬件)中进行分页
# Linux分页
在Linux中,采用三级页表结构。
- 普通页表实现时的问题:
- 32位OS(4G空间),每页4K,页表每个记录占4个字节
- 进程的页数:4G/4K = 1M个页
- 页表的记录数有:1M条记录
- 页表所占的内存:1M*4字节 = 4M
- 页表占页框数:4M/4K(页框每页4K) = 1K页框
因此难以找到连续的1K个页框来存放页表;页表全部装入会过度消耗内存。
解决思路:
- 将4M的超大页表存储到离散的1K个页框中
- 仅将页表的部分内容调入内存
# 二级页表
把超大的页表(4M)以页为单位分成若干个小页表,存入离散的若干个页框中。
例如:将超大页表(4M)划分为1024个小页表,每个页表的大小为4K,此时1个小页表刚好占用1个页框。
为了管理小页表,设置一个叫页目录的表,记录每个小页表的存放位置(即页框号)。
Windows NT采用了二级页表的结构
# 二级页表地址映射特点
- 访问数据需要三次访问:页目录,页表,最终单元
- 页目录调入内存
- 页表按需调入主存
- 页面、页表、页目录大小都刚好4K(占1个页框)
# Linux三级页表结构
# Linux对段的支持
Linux将4G的虚拟空间划分为两个部分,用户空间和内核空间。用户空间3G:从0到0xBFFFFFFF,内核空间1G:从0xC0000000到0xFFFFFFFF
- Linux四个范围一样的段:0~0xFFFFFFFF(4G)
- 各段属性不同
- 内核段特权级为0
- 用户段特权级为3
- 作用
- 利用段机制隔离用户数据和系统数据(保留段的等级保护机制)
- 简化逻辑地址到线性地址的转换(可以直接将虚拟地址当作线性地址,两者完全一致)