1. 几个问题
1.在linux和windows等系统中,都是需要开启MMU的,也就是内存管理单元。使用MMU有什么必要性?
答:在单片机中,我们所有的代码都是自己管理的。简单的说,单片机的所有代码,都是直接烧写进去,然后就跑起来。当然,在stm32这种单片机中,我们可以烧写一个程序,然后在运行的过程中,动态的再去加载另外一个程序,这归结起来,其实还是只有程序,一旦一个程序崩溃,整个系统将会完蛋。而使用mmu的好处在于,内核只需要管理好进程,进程间通信,做好物理内存分配,保证内核的稳定性,那么应用程序的开发和内核的开发就是分开的,应用程序的崩溃并不会导致内核的崩溃!
说到这里,就不得不提uClinux,它也分为内核开发和应用程序开发,但是有一点需要注意的是,应用程序可以访问物理内存。内核也可以直接访问物理内存,所以,它其实仍然是一种裸奔,随时有可能面临系统崩溃的问题。
2.MMU是怎么起效的?
答:处理器有4个内存管理寄存器,分别为:GDTR、LDTR、IDTR和TR。如下所示:
到这一个部分,我贴出在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):
CR0寄存器能开启段保护和页保护。需要注意的是,8086 CPU只支持分段和段页式,具体原因可以查看下面的图。
CR2~CR3用于分页机制。CR3保存了页目录的基地址。CR2用于报告页异常出错线性地址的信息。
3.分段和分页是怎么做到每个进程互不干扰?
首先看怎么从逻辑地址到线性地址。如下图所示:
这里就有两个重要的内容:段选择符与段描述符。我贴出以下图片:
上面说的是段描述符的通用格式,根据上面列出的代码,可以一段段来分析。
第一段:
.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,参考下图:
说明这部分是代码、执行/可读。
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,表示的是数据、可读/写。
接着再看线性地址到物理地址: