1. linux-0.11的保护模式分析


1. 几个问题

1.在linux和windows等系统中,都是需要开启MMU的,也就是内存管理单元。使用MMU有什么必要性?

答:在单片机中,我们所有的代码都是自己管理的。简单的说,单片机的所有代码,都是直接烧写进去,然后就跑起来。当然,在stm32这种单片机中,我们可以烧写一个程序,然后在运行的过程中,动态的再去加载另外一个程序,这归结起来,其实还是只有程序,一旦一个程序崩溃,整个系统将会完蛋。而使用mmu的好处在于,内核只需要管理好进程,进程间通信,做好物理内存分配,保证内核的稳定性,那么应用程序的开发和内核的开发就是分开的,应用程序的崩溃并不会导致内核的崩溃!
说到这里,就不得不提uClinux,它也分为内核开发和应用程序开发,但是有一点需要注意的是,应用程序可以访问物理内存。内核也可以直接访问物理内存,所以,它其实仍然是一种裸奔,随时有可能面临系统崩溃的问题。

2.MMU是怎么起效的?

答:处理器有4个内存管理寄存器,分别为:GDTR、LDTR、IDTR和TR。如下所示:


1. linux-0.11的保护模式分析_第1张图片
四个寄存器

到这一个部分,我贴出在linux-0.11上是如何加载寄存器值的(引用于setup.s中):

    lgdt    gdt_48      ! load gdt with whatever appropriate

gdt:
    .word   0,0,0,0     ! dummy

    .word   0x07FF      ! 8Mb - limit=2047 (2048*4096=8Mb)
    .word   0x0000      ! base address=0
    .word   0x9A00      ! code read/exec
    .word   0x00C0      ! granularity=4096, 386

    .word   0x07FF      ! 8Mb - limit=2047 (2048*4096=8Mb)
    .word   0x0000      ! base address=0
    .word   0x9200      ! data read/write
    .word   0x00C0      ! granularity=4096, 386


gdt_48:
    .word   0x800       ! gdt limit=2048, 256 GDT entries
    .word   512+gdt,0x9 ! gdt base = 0X9xxxx

简单分析一下:
gdt_48:中给出的是gdt寄存器需要加载的值。根据上面的贴图,可以分析出0x800计时16位的表长度。既然是16位的,那么这个表最大可以存储64k字节的数据,但是又由于gdt的表(也就是段)每一项是8字节的,所以总共可以存储8192项的段。

这里给出的0x800=2048B=256项的段。后面的.word 512+gdt,0x9表示的是32位线性基地址。很容易算出来,gdt表是在bootsect.s之后,bootsect.s存储在0x9000之后的512字节之内,从这里也可以看出标号其实是相对地址。所以整个表示起来就是基地址与表的大小。

另外还有四个寄存器(CR0~CR3):


1. linux-0.11的保护模式分析_第2张图片
CR0~CR3

CR0寄存器能开启段保护和页保护。需要注意的是,8086 CPU只支持分段和段页式,具体原因可以查看下面的图。


1. linux-0.11的保护模式分析_第3张图片
分段与段页模式

CR2~CR3用于分页机制。CR3保存了页目录的基地址。CR2用于报告页异常出错线性地址的信息。

3.分段和分页是怎么做到每个进程互不干扰?

首先看怎么从逻辑地址到线性地址。如下图所示:


1. linux-0.11的保护模式分析_第4张图片
逻辑地址到线性地址的转换

这里就有两个重要的内容:段选择符与段描述符。我贴出以下图片:

1. linux-0.11的保护模式分析_第5张图片
断选择符结构
1. linux-0.11的保护模式分析_第6张图片
段描述符结构

上面说的是段描述符的通用格式,根据上面列出的代码,可以一段段来分析。

第一段:

    .word   0,0,0,0     ! dummy

这表示gdt表中的第一个段,可以不使用,可以为空,但是要注意的是,在程序执行时,会产生一个一般保护性异常。

第二段:

    .word   0x07FF      ! 8Mb - limit=2047 (2048*4096=8Mb)
    .word   0x0000      ! base address=0
    .word   0x9A00      ! code read/exec
    .word   0x00C0      ! granularity=4096, 386

共8个字节,段限长分为两个部分=0x007ff=2047,G=1,标志按照4KB页颗粒度,处理器就会把当前值乘以一个因子4K,又由于此时使用段限长检查偏移值的时候,并不会检查偏移值的低12位,所以其实能检测的值为8MB。
而基地址总共占三部分,合起来的基地址其实是0。
TYPE=1010,参考下图:


1. linux-0.11的保护模式分析_第7张图片
type字段
1. linux-0.11的保护模式分析_第8张图片
代码段或数据段的TYPE描述

说明这部分是代码、执行/可读。

P=1,DPL=0,S=1,合起来表示段存在、使用的是代码或者数据段,DPL在特权级0上,为最高特权级。

G=1,D/B=1,AVL=0,其中,D/B为1表示的是32位的代码和数据段。

第三段:

    .word   0x07FF      ! 8Mb - limit=2047 (2048*4096=8Mb)
    .word   0x0000      ! base address=0
    .word   0x9200      ! data read/write
    .word   0x00C0      ! granularity=4096, 386

与上面的相同,但是TYPE=0x02,表示的是数据、可读/写。

接着再看线性地址到物理地址:

1. linux-0.11的保护模式分析_第9张图片
页目录和页表的格式

你可能感兴趣的:(1. linux-0.11的保护模式分析)