2.2 linux存储管理-地址映射全过程

来源:互联网 发布:淘宝网买女式鞋 编辑:程序博客网 时间:2024/04/29 05:36
Linux采用页式存储管理:
页式管理的优点(相比段式管理):1)页面固定便于管理;2)按页换进换出,效率高
Intel由于历史原因,其i386 CPU(仅限于i386)硬件结构需要先进行段式映射(其实毫无必要),才能使用页式管理。Linux的做法是让段式管理不起作用

段寄存器的格式定义:

RPL:请求特权级别,分4级,0最高(对应内核态),3最低(对应用户态)

GDTR表项含义:



important: 在程序运行时,进程已经建立,此时整个映射机制都已经准备完毕
1) 段寄存器和GDT,进行段式映射
2) CR3寄存器、PGD,进行页式映射
3) 这些地址映射过程由硬件实现

1. 进行段式映射,段式映射的顺序:
假设访问的虚拟地址为virt_addr
1.1 找到virt_addr所在段(数据,代码,堆栈等),并访问相应的段寄存器(包含index)
1.2 根据段寄存器的值和GDTR,找到该进程的GDTR表项
1.3 GDTR表项中包括段基址(全为0),访问权限等信息,得到线性地址
线性地址 linear_addr = virt_addr + 0 (段基址) == virt_addr
notice:
   1)由上可知,Linux中段式映射完全不起作用,虚拟地址即为线性地址
   2)此时,所有进程共用相同GDT表项

1.1 段寄存器(里面存放的叫段选择符)
linux 有4种不同的段寄存器的值,两种用于内核、两种用于用户进程,为
进程的DS,ES,SS都为 __USER_DS,CS为__USER_CS。(参考 include/asm-i386/processor.h),Intel的意图是将进程的分成代码、数据、堆栈、扩展等段,但是Linux不进行区分,Linux内核中堆栈和数据是同一段

(参考 include/asm-i386/segment.h)
从上图可以看到, Linux全都是用GDT,基本不使用LDT(只有带 VM86下才使用,比如wine)
四个段寄存器的值,指向4个GDT表项,分别为2,3,4,5

1.2 GDTR表项(里面存放的叫段描述符)

(参考 arch/i386/kernel/head.S)
上图给出了4种段的情况:
相同:段基址(都为0),段上限(1M),段步进(4K),32位,在内存
不同:代码段还是数据段,可执行还是可写(都可读),访问权限(0|3),是否被访问过
(参考上面的 段描述符定义 图)
GDT的大小为8192 * 32bit ,而每个GDT表项为64bit,所以最多可以存4096个表项;但是由于表项0为0,表项1不使用,表项2,3,4,5分别用于存放内核、进程的4种段信息,所以可用的表项有4096 - 6 = 4090 ,所以32位Linux 最多支持4090个进程


2.进行页式映射
2.1 访问CR3(存放了PGD的物理地址),查找PGD(第1次访存),获取PD的物理地址
2.2 查找PT(第2次访存),获取Page的物理基地址
2.3 查找Page(第3次访存),利用页内偏移,获取实际物理地址

线性地址结构:
线性地址为32位
 31-22:PGD中的索引,用来获取PD的物理地址
 21-12:PT中的索引,用来获取Page的物理地址
 11-0:Page中的索引,用来获取 线性地址 对应的 物理地址

2.1 第1次访存
CR3 存放了PGD物理地址,通过他我们可以访问当前进程的PGD,利用PGD索引,找到条目i

因为内存是4K对齐的,而PD大小为4K,所以只需要32位地址的高20位即可的到PT的物理基地址
条目i的结构:
31-12:用来存放PD的物理基地址的高20位
11-0:一些标志位,0位为P标志位(1代表,该页面表在内存中)

2.2 第2次访存
    这个过程和第一次访存基本一致,不过使用的是PT索引,获取了Page的物理基地址

2.3 第3次访存
    第2次访存已经获取了Page的物理基地址phys_base,phys_base加上线性地址的最低12位,就获得了virt_addr对应的phys_addr