LINUX内核研究----地址映射的全过程

时间:2024-05-22 11:06:45

内核空间的地址映射:

物理地址和虚拟地址之间相差0XC0000000

虚拟地址 –0XC0000000 ==物理地址

物理地址 +0XC0000000 ==虚拟地址

并且内核的页面不交换,不进行换出到磁盘中。因为内核要随时准备服务。

Inter X86 32 cpu linux  kernel

实模式:

CPU一上电强制进去实模式,只能访问2^20==1M的内存且没有程序间隔离,没有内存的读写保护,内存的越界保护。

Intel在8086中设置了4个寄存器:

CS(代码段寄存器),DS(数据段寄存器),SS(堆栈段寄存器),ES(扩展段寄存器)

每个段寄存器都是16位,对应每个段的起始地址的高16位。这是由于物理地址被划分成一段一段的,每个内存段都是16的整数倍,即在20位的地址中,低四位全为0,没有必要存储,所以只用这四个寄存器来保存相应物理地址的高16位。

 

   因此,实模式下地址映射表达式为:(假如寻找数据的真实物理地址)

                          DS<<4 + IP = 物理地址

  DS<<4得到真实的20位段基地址(左移四位相当于*16),加上IP寄存器存放的偏移量(逻辑地址),就可以找到数据所存放的位置,这里的数据地址为物理地址。因此,在实模式下,可以直接通过修改段寄存器的内容访问内存中的任何一个地址单元,不受任何权限控制,没有保护措施

内核的代码加载从0X100000(1M)的地址之后开始加载,前1M存放BIOS代码和其他重要的内核引导代码。

保护模式:

Inter 80386的32位CPU为讨论对象,由于Intel80386芯片要兼容以前设计,保留以前16位的段寄存器,同时又增添了两个新的寄存器:FS和GS。设计的基本思路:设计一个数据结构,包含基地址,地址段长度及访问权限等信息,在保护模式下需要改变段寄存器的功能,把原来存放段基址的功能改变成存放一个数据结构的指针。

    基本过程:

       (1)根据指令的性质判断使用哪一个段寄存器;数据段则用DS寄存器

       (2)在段寄存器中找到相应的数据结构;

       (3)在此数据结构中找到段基地址;

       (4)根据指令地址(偏移量)和地址段大小比较,判断是否越界;

        (5)根据指令权限与数据结构中的权限对比,判断是否越权;

       (6)段基址+指令地址得到物理地址。

实现过程:

增加了两个寄存器:

GDTR(global descriptortable register)全局的段描述表寄存器,所有进程所共享的.存放GDT在物理内存中的起始地址

LDTR(local descriptortable register)局部的段描述表寄存器,每个进程私有的,已经不使用。

LINUX内核研究----地址映射的全过程

16位段寄存器的高13位index存放GDT数组的下标索引。 2^13== 8k,已经有12项被内核系统预留下来,用户进程使用的有8K – 12==8192个全局或局部的段描述表项,它与GDTR或LDTR保存的其实地址相加才得到描述表项的起始地址。

第三位TI:0表示在GDT存,1表示LDT存

段寄存器的低两位RPL表示CPU的特权级00表示内核态,11表示用户态

 

段描述符表(GDT):在物理内存中保存,表中的每一项为8字节共64位,存放段基址,段大小和其他读写信息,具体结构如图所示:

LINUX内核研究----地址映射的全过程

起始地址32位:B31-B24,B23-B16,B15-B0

长度20位:L19-L16,L15-L0

长度单位G,为0代表单位为字节,为1时代表页面

保护模式下的地址映射:

内存的分段式映射:GDT[DS>>3].BaseAddr + IP寄存器 ===线性地址

注:假设要找数据段上的地址,故使用DS寄存器。GDTR保存了GDT的地址。BaseAddr: 保存在GDT表项中的B31-B24,B23-B16,B15-B0等位。IP寄存器保存了偏移量。

DS>>3取得GDTR表项的下标,GDTR[DS>>3].BaseAddr找到该表项的基地址,再加上逻辑地址,得到该数据的线性地址。此处不为物理地址是还要进行内存分页机制。

LINUX内核研究----地址映射的全过程

 


得到线性地址后:


LINUX内核研究----地址映射的全过程


如果内核没有开启线性地址的话:线性地址就是物理地址。

如果内核开启了分页机制线性地址就是虚拟地址。

虚拟地址经过多级的页表映射到物理地址,接下来是多级页表映射过程也叫内存的分页式映射。

CPU寄存器:

CR0:该寄存器的最高位PG位为1表示开启了内存分页,0没有开启

CR2:该寄存器访问发生缺页异常的虚拟地址

CR3: 该寄存器记录当前进程的页目录的起始地址

CR4: 该寄存器的PAE位,物理地址扩展。0表示未开启,1表示开启

内存的分页式映射:

32位需要二级页面映射1024*1024*4K=4G,36位三级页面映射,64位四级页面映

上面过程得到的线性地址也叫32位虚拟地址0x0018ff44分成三份   

LINUX内核研究----地址映射的全过程

LINUX内核研究----地址映射的全过程

CR3寄存器存放了当前进程的也目录表的起始地址

高10位保存了页目录中的下标,每个页目录项保存了页表的起始地址

中间10位保存了页表中的下标,每个页表项保存了物理地址的起始地址

低12位是在4K的物理页上的偏移量                                            

 

分页式映射关系:

页目录PG[下标]==PT的首地址地址

页表PT[下标]==物理页的首地址其实也是一个数组下标

页表PT[下标]+低12位的偏移量==真实物理地址

因为页表PT首地址和物理页的起始地址都是4K的整数倍,所以二进制的后12位都是0不需要存,而在页目录中每项的低12位用来存权限。

在页表中的每项的低12位也是用来存权限,其中最低的一位是0表示物理页在交换分区中,如果是1则表示页面在物理内存中。

物理页面在内核中是用动态数组来管理的mem_map_t *mem_map;

页表中保存的就是这个数组的下标,物理页面的框号也是这个数组下标,4K整数倍的后12位都是0不需要保存,相互转换的话是通过移位运算。

LINUX内核研究----地址映射的全过程