x86保护模式笔记


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

                                                       以下为自己的总结的x86保护模式知识

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

x86虚拟地址空间64TB,通过分段/分页映射到物理内存。而linux 实际上只是有限的使用了分段机制(比如使用了段的特权级保护),并没有利用分段机制实现虚拟化,所以linux下每个进程的虚拟空间,或者说编程空间只有4GB,而不是64TB。  假设现在我们要利用x86的分段机制实现虚拟化,将虚拟空间变成64TB,那么与分页机制实现的虚拟化相比,有什么不同的地方呢?讨论如下:

在分页机制下,每个页大小为4KB,对于x86来说,一个页表项一旦建立,4KB的物理内存就已经被 “ 使用 ”了,即使实际上你只使用了一个字节。。因此,系统内能同时存在的页表项的个数x4KB,不能大于实际物理内存,其余的页表项必须标识为不存在。那么在分段机制上,段的大小是不固定的,但是一旦在段描述符中确定大小后(假设为N个字节),与分页机制一样,即使你只用了一个字节,对x86来说,N个字节已经被 “ 使用 ”了。。因此,系统内能同时存在的段的个数是不固定的,但他们的大小之和不能大于系统物理内存。

x86有32位地址线,对于linux来说 由于没有利用分段机制实现虚拟化,虚拟地址的选择子都是操作系统填的,程序员只需要关注偏移地址,不管选择子是多少,段描述符中基地址都为0,也就是虚拟地址统一映射到偏移地址,这样一来,16位选择子不参与地址映射了,16+32位地址等于变成了32位地址,64TB虚拟空间被压缩到了4GB线性空间(也就是说linux下虚拟地址的偏移部分等于线性地址),再由分页对这4GB空间进行虚拟化。。 假设我们要让16位地址参与映射,实现64TB虚拟空间,就要注意线性地址不能 “ 冲突 ” (在linux中,实际上线性地址是“ 冲突 ” 的,比如选择子为1,偏移为0xffff,和选择子为2,偏移为0xffff,都映射到一个线性地址0xffff),具体来说就是 线性地址由选择子和偏移地址共同唯一确定,但毕竟只有32位地址线,映射完4GB线性地址后,多出来的64TB-4GB的虚拟地址如何表示呢,答案是无法表示,那么会有问题吗,不会!因为这部分地址所在段的存在位一定是0(见前面第二段)。分页机制总的虚拟空间只有4GB,正好够32位数来表示,所以整个虚拟空间都能用线性地址表示出来,而在分段机制中,(64TB-4GB)大小的虚拟地址无法表示(这不一定是4GB~64TB这段地址,而是64TB虚拟地址映射4GB线性地址后,剩下的那块64TB-4GB大小的地址),但反正也不存在,所以对虚拟化实现没有影响,要用时,释放掉之前使用的一些线性地址,把相同大小的虚拟地址映射过去就行了(总之最多映射4GB),总之,64TB虚拟空间中,能表示为线性地址的就4GB,其余的就无法表示且存在位一定为0。

到此为止。


程序流的转移:

x86程序流的转移主要有以下几种方式: 子程序调用,中断和任务切换。对于x86,只要当前tss段没有切换,那么x86就认为任务没有切换,因此,段间子程序调用、非任务门的中断、调用门(尽管linux没有调用门)这些转移对于x86来说都是“ 任务内转移 ” ,从这个角度划分,我把程序流的转移分为下面几种。

首先大类别上分为任务内转移与任务间转移,任务间转移就是任务切换,这里不谈(参考x86任务切换),所以现在主要讨论任务内转移;任务内转移又分为段内转移与段间转移,段内转移很简单,其实就是call或者jump进行短跳转,指令操作数只给出偏移,默认在当前段内跳转。如果是call指令,cpu压入eip,执行ret时返回到原程序。。段间转移又分为特权级切换的段间转移和特权级不变的段间转移,对于特权级不变的段间转移,一般就是call,jump长跳转,或者是调用门,如果是直接call或者call调用门,cpu压入cs,eip,执行retf远返回到原程序。对于需要改变特权级的段间转移,就需要切换到对应特权级的堆栈上,这里提一下,x86中,代码分为一致性代码和非一致性代码,程序可以转移到特权级相等或更高的一致性代码段(CPL不变),却只能转移到相同特权级的非一致性代码段中(除非用call调用门或者中断/异常的方式,可以转移到特权级更高的非一致性代码段中,此时CPL变为目标代码段的DPL)。因此,对于会改变特权级的段间转移,只有一种可能,就是低特权级段转向另一个高特权级段。这时,需要切换到高特权级堆栈,这个堆栈保存在任务的tss段中,x86检测到发生特权级上升的时候,会从那里拿到新的高特权级对应的堆栈,加载这个堆栈到ss,esp中,并将原来的堆栈压入这个新栈,如果是call调用门的方式引起的转移,还会压入指定个参数(个数在调用门描述符中指定)到新堆栈(这些参数也在原堆栈上),最后压入返回地址cs,eip。

那么如果是中断引起的特权级升高的段间转移呢?首先从tss段拿到对应新堆栈,加载到ss,esp中,并将原堆栈值压入新堆栈,这和之前通过call调用门转移到高特权级段是一样的,但是中断不传递参数,所以没有参数入栈,另外中断与普通程序不同,需要保存标志寄存器(因为中断的随机性),所以EFLAGS入栈,最后cs,eip入栈。。

再说一下,以上转移在x86眼中都是同一个任务内的转移哦。。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值