在MMU工作原理1 中,我们只是描述了1级页表。如果我们有一个32位的地址空间,每个页框是4k,和一个4Byte的 页表,则也需要4M(4* 2^(32-12))的页表驻留在存储器中。
如下图(虚拟存储器中)。虚拟存储器中,0-2047:表示已经分配出去2k个代码和数据的VM页,接下来6k个未分配的vm页,接下来1023个未分配的页,最后一个页用来做栈。一级页表中的每个PTE负责映射虚拟地址的4M的片,然后二级页表和原来的一样,每个PTE负责一个4k的页。按照每个PTE是4Byte,地址空间是4g算,则存储一级页表是用4kB,每个二级页表也是4kB。
这样的好处是:
1.如果1级页表是空的,那么二级页表就没有必要存在。对于一般的程序,不可能完全用4g空间,这样将做巨大节省。
2.只有1级页表在主存中。2级页表只有在需要时创建,页面调入或调出。
前提条件:
1.存储器字节寻址。2.存储器访问是1字节。
假设我们的虚拟地址和TLB如下:
vpo(虚拟页offset):2^6 = 64byte
(虚拟页号):8位表示。2^8 = 256个条目。
每个行TLB为4组:TLBI:表示每组中的4个中的其中一个的标记。 TLBT:剩下的高6位作为位。
PTE(页表条目)如下:
物理地址如下:
ppo:物理页offset
ppn:物理页号
当cpu发出读0x34d4命令时,首先解析VPN为0xf,然后检查TLB中是否含有,0xf对应TLB的 TLBI(每一行的标记)0x3,TLBT(每列的位)为0x3,此时查找TLB表,valid且找到PPN为0D + (0x14)---->物理地址为0x354.
继续解析0D的含义。下图CT(标记位):CI(缓存组索引 )CO:(缓存组偏移)
cache如下:按照上面推测,则得知要的值是36;
下图演示了完整过程:
每个进程都有自己的虚拟地址空间,思考:为什么32位的地址空间从0x08048000开始呢?因为以下内存保存了一些库的.so文件。因为每个进程都共享kernel的的代码和数据,所以不同进程的内核虚拟存储器中的一些区域映射到相同的物理页(内存共享)。linux将虚拟存储器组织成一些区域的集合,如代码段,数据段等等。每个存在的虚拟页都保存在某个区域中。
下面理解下kernel的的数据结构。
task_struct:任务结构,包含了当前进程的所有信息,如PID,进程状态,等等。保存在thread_info中,而thread——info保存在内核栈的尾端,方便current宏进行查找。
mm_struct: pgd:指向一级页表的基址。mmap:描述了当前虚拟地址空间的一个区域。
vm_start:指向这个区域的起始处。
vm_end:指向这个区域的结束。
vm_port:这个区域所有页的权限位。
vm_flags:public还是private信息。
首先理解一下写时拷贝技术。有两个进程共享了一段私有数据的物理内存,此时私有页表标记位只读。当进程试图修改此段共享物理页的时候,就会触发一个保护故障。就会进行新的拷贝。
当fork被当前进程调用时候,就会产生一个新的进程,新的PID。但是mm_struct,区域结构等都是直接copy。两个进程每个页面都标记为只读,当进程发生写操作时候,就产生了写时拷贝。
mmap函数:要求内核创建一个新的虚拟存储器区域,并将文件描述符fd指向的一片连续区域映射到该区域。