您的位置:首页 > 运维架构 > Linux

Linux中地址映射的全过程

2016-06-03 17:20 302 查看
Linux内核采用页式存储管理。虚拟地址空间划分成固定大小的“页面”,由MMU在运行时将虚拟地址“映射”成某个物理内存页面中的地址。

CPU的硬件结构

页式存储管理与段式存储管理所要求的硬件支持不同。

i386 CPU一律对程序中使用的地址先进行段式映射,然后才能进行页式映射。

对于其它的处理器,比如说M68K、PowerPC等,不存在段式映射这一层。

Linux内核采取的办法是使段式映射的过程实际上不起什么作用。

在elf格式的可执行代码中,ld总是从0x8000000开始安排程序的“代码段”,对每个程序都是这样。

程序在执行时在物理内存中的实际位置则就要由内核在为其建立内存映射时临时作出安排,具体地址取决于当时所分配到的物理内存页面。

程序运行过程中,地址0x08048368是一个程序的入口,在执行过程中由CPU中的“指令计数器”EIP所指向,在代码段中。

i386 CPU使用代码寄存器CS的当前值来作为段式映射的“选择码”,用它作为在段描述表中的下标。

段寄存器的格式:

Index TI
RPL

Intel的设计意图是内核用GDT而各个进程都用其自己的LDT。最低两位PRL为所要求的特权级别,共分4级,0为最高。

内核在建立一个进程时都要将其段寄存器设置好,有关代码在include/asm-i386/processor.h中。

在Linux内核中堆栈段和数据段是不分的。

实际上,在Linux内核中基本上不使用局部段描述表LDT。LDT只是在VM86模式中运行wine以及其它在Linux上模拟运行Window软件或DOS软件的程序中才使用。

初始的GDT内容是在arch/i386/kernel/head.S中定义,其主要内容在运行中并不改变。

段的类型、DPL,内核为最高的0级,用户为最低的3级。

段式映射过程中所有进程全都共用一个GDT。

每个进程都有其自身的页面目录PGD,指向这个目录的指针保持在每个进程的mm_struct数据结构中。

每当调度一个进程进入运行的时候,内核都要为即将运行的进程设置好控制寄存器CR3,而MMU的硬件则总是从CR3中取得指向当前页面目录的指针。

CPU在执行程序时使用的是虚拟地址,而MMU硬件在进行映射时所用的则是物理地址。这是在inline函数switch_mm()中完成的。

不管什么进程,一旦进入了内核就进了系统空间,都有相同的页面映射。

i386 CPU以线性地址的最高10位为下标去页面目录中找到其目录项。

这个目录项中的高20位指向一个页面表。

每个页面表占一个页面,4K字节边界对齐,其起始地址的低12位一定是0。所以把32位目录项中的低12位挪作它用,其中最低位为P标志位,为1时表示该页面表在内存中。

线性地址的中间10位,CPU以此为下标在已经找到的页面表中找到相应的表项。

页面表项的P标志位为1时表示所映射的页面在内存中。

32位的页面表项中的高20位指向一个物理内存页面。

加上线性地址中的最低12位,就得到最终的物理内存地址。

在页面映射过程中,i386 CPU要访问内存三次,第一次是页面目录,第二次是页面表,第三次是访问真正的目标。

虚存的高效实现有赖于高速缓存(cache)的实现。

为了能在Linux内核上仿真运行采用段式存储管理的Windows或DOS软件,提供了两个特殊的、与段式存储管理有关的系统调用。

modify_ldt(int func, void *ptr, unsigned bytecount);

这个系统调用可以用来改变当前进程的局部段描述表。

vm86(struct vm86_struct *info);

用来在Linux上模拟运行DOS软件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: