在上期《虚拟化与云计算硬核技术内幕 (6) —— 妇女能顶半边天》中,我们理解了两个事实:
- 在新中国,妇女以在人民解放中的卓著功勋,顶起了半边天;
- x86体系在支持VMX之后,通过实力证明了自己可以支持虚拟化;
我们也遗留了一个问题:
通过什么样的手段,可以阻止虚拟机A访问属于虚拟机B或属于宿主机的内存地址空间呢?
我们知道,在某些理发店、房产中介等机构,为了屏蔽人和人之间个性的差异,也为了帮助一些普遍存在的低素质的人做违反社会道德的事情后,逃避相关的责任,这些机构引入了“花名”的机制,让每个人除了自己的物理名字以外,还拥有一个虚拟的“花名”。Intel的处理器设计师也借鉴了这一想法,引入了保护模式,给内存起了“花名”。
在有了保护模式后,处理器访问的物理地址(处理器读写RAM芯片时,向RAM芯片的地址线发送的地址)和逻辑地址(程序指令中的地址)之间就多了一层映射关系,如下图所示:
在386以上的处理器中,内存地址是32bit的,也就是最多可以寻址到4GB。每个进程可以看到自己的寻址空间。这些寻址空间又是按段(segment)分配的。按段的不同用途可以分为几类:
text段:可执行代码在text段;
data段:静态数据和动态分配的堆在data段;
stack段:局部变量以及函数之间传递参数使用的堆栈在stack段;
段地址加上偏移量,就可以得到“线性地址” (Linear Address)。
实际上,处理器会将线性地址进行拆分,如下图所示:
图中,线性地址的bit0-bit11的12bit,共4K空间,为最终偏移量的指针,也就是指向每页内的偏移量。
bit12-bit21这10bit对应着1024个页。我们注意到,图中的bit12-bit21指向的是一个page table,说明它实际上是一个指向page table entry的指针。如这10bit的值为0000110100b,那么,处理器会从第36个entry读取实际的地址。
page table只有1024个,每个entry对应4K,那么,整机只能寻址4MB内存,这是显然不足以支持绝大多数应用程序运行的。在22-31这10个bit中,指向的page directory表解决了这一问题。实际上处理器在寻址时,会去page directory里面找到page table。
page directory在哪里呢?在cr3寄存器指向的RAM里面。因此,处理器的整个寻址过程为:
线性地址拆分->去cr3 page directory偏移量的地方找到page table->去page table page table偏移量的entry里面找到物理地址->访问物理地址。
这样一来,处理器就可以通过虚拟地址找到实际的物理地址了。
那么,怎么样可以确定处理器有没有访问物理地址的权限呢?
原来,Intel的处理器在访问一个内存页的时候,还需要处理器做一个判断此种行为是否有授权,以避免出现某些非正规企业的“破冰”行为那样侵犯了他人的边界。
这种判断的依据就储存在描述符表(Descriptor Table)中。Intel的描述符表有GDT(全局描述符表)和LDT(局部描述符表),描述符决定了当前代码能否有权限访问一个段。
图中,红框部分的四部分组成了这个段的基地址,而蓝框部分为这个段的长度。13和14位的DPL描述了这个段的特权级别。只有当前的特权级别高于13和14位的数字,才可以访问这个段。此外,R/W和A这两位决定了该段是否可以使用,是否可读写。
在引入虚拟化以前,这种机制是足以保证系统不出现越权访问的,但在虚拟化引入后,由于虚拟机操作系统GuestOS的内核也是在ring0下工作,它可以访问所有的段!
这就像某些非正规组织的“破冰”一样,超越了人与人应有的界限,甚至造成了针对人身权益的严重侵犯!
怎么样杜绝这一“破冰”现象呢?
请看下回分解。