备份内容哈哈哈

        CALL FAR

        JMP FAR

        栈切换过程:

        1.使用目标代码段的DPL到当前任务的TSS中选择一个栈,包括栈段选择子,栈指针

        2.从TSS读取选择的段选择子,栈指针,并用该选择子读取栈段描述符.

        3.检查栈段描述符的特权级,类型.

        4.临时保存当前栈段寄存器SS和栈指针ESP内容

        5.把新的栈段选择子,栈指针代入SS和ESP寄存器,切换到新栈

        6.将刚才临时保存的SS和ESP内容压栈.

        7.依据调用门描述符"参数个数"指示,从旧栈将所有参数复制到新栈.

        8.将当前寄存器CS和指令指针寄存器EIP压入新栈.

        9.从调用门描述符将目标代码段选择子和段内偏移传送到CS和EIP寄存器,开始执行被调用过程.

        控制返回:

        1.检查栈中保存的CS寄存器的内容,根据其RPL决定返回时是否需改变特权级别

        2.从当前栈读取CS和EIP寄存器的内容,针对代码段描述符,代码段选择子的RPL字段实施特权级检查.

        3.如远返回带参数,将参数和ESP寄存器当前值相加,以跳过栈中参数部分.

        4.如返回时,需改变特权级,从栈中将SS和ESP压栈值代入段寄存器SS和ESP.切换到调用者的栈.

        5.如远返回指令带参数,将参数和ESP当前值相加

        6.如返回时需改变特权级,检查DS,ES,FS,GS内容.

2.任务切换

        实模式下,内存最低地址段1KB是中断向量表.

        保护模式下,中断向量表不再使用,取而代之是,中断描述符表.它保存的是门描述符,包括中断门,陷阱门,任务门.

        调用门,从任务的局部空间转移到更高特权级的全局空间去执行.

        中断门,陷阱门允许在任务内实施中断处理,转移到全局空间去执行一些系统级的管理工作.

        当中断发生时,处理器用中断号乘以8作为索引访问中断描述符表.当它发现这是一个任务门描述符时,就知道应发起任务切换.于是,它取出任务门描述符;从任务门描述符取出新任务的TSS选择子;再用TSS选择子访问GDT,取出新任务的TSS描述符.

        转到新任务执行前,处理器先把当前任务的状态保存起来.

        处理器访问新任务的TSS,从中恢复各个寄存器的内容,包括通用寄存器,标志寄存器,段寄存器,指令指针寄存器,栈指针寄存器,局部描述符表寄存器等.

        最终,任务寄存器TR指向新任务的TSS,而处理器旋即开始执行新的任务.一旦新任务开始执行,处理器固件会自动将其TSS描述符的B位置"1",表示该任务的状态为忙.

        中断发生时,可执行常规的中断处理过程,也可进行任务切换.它们都使用iret指令返回.一个是返回一个任务内的不同代码段,一个是返回到被中断的任务.

        32位处理器的EFLAGS有NT位,意思是嵌套任务标志.

        每个任务的TSS都有一个任务链接域,可以填写为前一任务的TSS描述符选择子.如当前任务EFLAGS寄存器的NT位是"1",则表示当前正执行的任务嵌套于其他任务内,且能通过TSS任务链接域的指针返回到前一个任务.

        因为中断而引发任务切换时,取决于当前任务是否嵌套于其他任务内,其EFLAGS寄存器的NT位可能是"0",也可能是"1".

        对新任务的处理是,要把老任务的TSS选择子填写到新任务TSS中的任务链接域,同时将新任务EFLAGS寄存器的NT位置"1",以允许返回到前一个任务继续执行.同时,把新任务TSS描述符的B位置"1".

        用iret指令从当前任务返回到前一任务,前提是当前任务EFLAGS寄存器的NT位必须是"1".无论何时处理器碰到iret,它要检查NT位,如是0,表明是一般的中断过程,按一般的中断返回处理.即,中断返回是任务内的[中断处理过程虽然属于操作系统,但属于任务的全局空间];此位是1,则表明当前任务之所以正在执行,是因为中断了别的任务,故,应返回原先被中断的任务继续执行.此时,处理器固件把当前任务EFLAGS寄存器的NT位改为"0",把TSS描述符的B位改为"0".保存了当前任务状态后,用新任务的TSS恢复现场.

       通过远程CALL或JMP,指定任务的TSS描述符下,也能实现任务切换.

call 0x0010:0x00000000
jmp 0x0010:0x00000000

        处理器执行这两条指令时,先用指令中给出的描述符选择子访问GDT,分析它的描述符类型.如是代码段描述符,按普通的段间转移规则进行;如是调用门,按调用门规则执行;如是TSS描述符,或任务门,则执行任务切换.

        任务门描述符可安装在中断描述符表中,也可安装在全局描述符表或局部描述符表中.

        call指令发起的任务切换,当前任务TSS描述符的B位保持"1"不变,EFLAGS的NT也不变化.新任务的TSS的B位置"1",EFLAGS寄存器的NT位也置"1",表示此任务嵌套于其他任务中.同时,TSS任务链接域的内容改为旧任务的TSS描述符选择子.        

        

3.处理器在实施任务切换时的操作

        处理器用以下四种方法将控制转换到其他任务:

        1.当前程序,任务或过程执行一个将控制转移到GDT内某个TSS描述符的jmp或call.

        2.当前程序,任务或过程执行一个将控制转移到GDT或当前LDT内某个任务门描述符的jmp或call指令.

        3.一个异常或中断发生时,中断号指向中断描述符表内的任务门.

        4.在EFLAGS寄存器的NT位置位下,当前任务执行了一个iret.

        任务切换时,处理器执行以下操作:

        1.从jmp或call指令的操作数,任务门或当前任务的TSS链接域取得新任务的TSS描述符选择子.

        2.检查是否允许从当前任务切换到新任务.数据访问的特权级检查规则适用于jmp和call.异常,中断,[int n引发的除外]和iret引起的任务切换忽略目标任务门或TSS描述符的DPL.int n产生的中断,要检查DPL.

        3. 检查新任务的TSS描述符是否已经标记为有效,且界限也有效.

        4.检查新任务是否可用,不忙[对CALL,JMP,异常,中断发起的任务切换]或者忙,对以iret发起的任务切换.

        5.检查当前任务和新任务的TSS,及所有任务在任务切换时用到的段的描述符已经安排到系统内存中.

        6.如任务切换由jmp或iret发起,处理器消除当前旧任务的忙标志;如由call指令,异常,中断发起,忙标志保持.

        7.如任务切换由iret发起,处理器建立EFLAGS寄存器的一个临时副本并清除其NT标志;如由call, jmp, 异常或中断发起的,副本的NT标志不变.

        8.保存当前任务的状态到它的TSS中.处理器从任务寄存器中找到当前TSS的基地址,将以下寄存器的状态复制到当前TSS中:所有通用寄存器,段寄存器中的段选择子,刚才那个EFLAGS寄存器的副本,及EIP.

        9.如任务切换由call指令,异常,中断发起,处理器把从新任务加载的EFLAGS寄存器的NT标志置位;如由iret或jmp发起的,NT标志位的状态对应着从新任务加载的EFLAGS寄存器的NT位.

        10.如任务切换由call指令,jmp指令,异常或中断发起的.处理器将新任务TSS描述符中的B置位,如由iret发起的,B位保持原先的置位不变.

        11.用新任务的TSS选择子和TSS描述符加载任务寄存器TR.

        12.新任务的TSS状态数据被加载到处理器.

        13.与段选择子相对应的描述符在经过验证后也被加载.

        14.开始新任务

16.分页机制和动态页面分配

        每个段描述符有A位,每当访问一个段时,处理器会将其置位.

        A位的清零由操作系统定时进行,它可以借此机会统计段的访问频度.

        当内存不够用时,它可以将那些较少访问的段换出到磁盘上,以腾出空间给马上要运行的段使用.一旦某个段被挪到磁盘上,操作系统应将其描述符的P位清零.过一段时间,这个段又被访问时,因其描述符的P位是"0",处理器引发段不存在异常[中断号11].

        它会用同样的方法腾出空间,将这个段的内容从磁盘调入内存.这类中断返回时,处理器会再次执行引发异常的那条指令.

        分页功能,从总体上说,是用长度固定的页代替长度不一定的段.由处理器固件来作这件事,可以使速度和效率最大化.

16.1.分页机制概述

16.1.1.简单的分页模型

        处理器中有负责分段管理的段部件.

        每个程序或任务都有自己的段,这些段都用段描述符定义. 

        内存的分配涉及段空间的分配和页分配.

        分页模式下,操作系统可创建一个为所有任务共用的4GB虚拟内存空间,也可为每个任务创建独立的4GB虚拟内存空间.

        当一个程序加载时,操作系统既要在左边的虚拟内存中分配段空间,又要在右边的物理内存中分配相应的页面.因此,步骤一是寻找空闲的段空间,该空间既没有被其他程序使用,也没有被同一程序内的其他段使用.

        页的最小尺寸是4KB.因此,8200字节的段需要占用3个页面.如果允许页共享,多个段或多个程序可用同一个页来存放各自的数据.

        在分段后,操作系统的任务就是把段拆开,分别映射到物理页.注意,段必须是连续的,但不要求所分配的页都是连续的,挨在一起的.

        有些程序关闭了页面要回收.几个回合下来,空闲的页零零散散分布在物理内存中,一般不会是连续的.分配页面时,操作系统会搜索空间的页,并分配给程序使用,所分配页面的总长度要大于等于段长度.

        4GB虚拟内存空间不可能用来保存任何数据,因为它是虚拟的.只是用来指示内存的使用情况.当操作系统加载一个程序并创建为任务时,操作系统在虚拟内存空间寻找空闲的段,并映射到空闲的页.然后,真正开始加载程序时,再把本属于段的数据按页的尺寸拆开,分开写入对应的页中.

        从段部件输出的是线性地址,或叫虚拟地址.为根据线性地址找到页的物理地址,操作系统需维护一张表,把线性地址转换为物理地址.

        如下图,因为有1048576个页,所以转换表也有1048576项.每项占据4字节,内容为页的物理地址.因为页的尺寸是4KB,故线性地址的低12位可用于访问页内偏移,高20位可用于指定一个物理页.因此,把线性地址的高20位当成索引,乘以4,作为表内偏移量,从表中取出一个双字.得到该线性地址所对应页的物理地址.

        当程序加载时,操作系统会首先在虚拟内存中分配段.然后,根据段需要分成多少页,来搜索空闲页面.当段较大时,要按页的尺寸分成好几个地址区段,操作系统用每个区段的首地址,取高20位,乘以4,作为偏移量访问表格,并将分配给该区段的页的物理地址写入该表项.最后,把原本需要写入每个区段的程序数据,写到对应的页中.

00

        页式内存管理中,页面的管理和分配是独立的,和分段及段地址没关系.操作系统要做的,就是寻找空闲页面,把它分配给需要的段,并将页的物理地址填写到映射表内.

        线性地址,线性地址空间和页面分配机制没关系.

        一般,每个任务都可以拥有4GB的虚拟内存空间;同时,每个任务都有自己的页映射表.

        虽然有多个任务,每个任务都有自己的4GB虚拟内存空间,但,在整个系统中,物理页面是统一调配的.考虑这样一个情景,任务A有一个段,段基地址为0x00050000,段长度为3000字节,操作系统为它分配了一个物理地址为0x08001000的页.过了一会儿,另一个任务B加载了,它也有一个段,段基地址也是0x00050000,段长度为4096字节.

        此时操作系统则分配另一个不同的,物理地址为0x00700000的页.

        这种情况下,在任务A内访问线性地址0x00050006,访问的其实是物理地址0x08001006;在任务B内访问同样的线性地址时,访问的其实是物理地址0x00700006.

        操作系统可将暂时不用的页退避到磁盘,调入马上就要使用的页,通过这种手段来实现分页内存管理.

        以上,就是基本的段页式内存管理机制.

16.1.2.页目录,页表,页

        4GB虚拟内存,按4KB的页划分,需要1048576个页.每个页实际的物理页起始位置用4字节存储,则需4M内存.

        分页结构层次化主要手段是不采用单一的映射表,代之页目录表,页表.

        4GB虚拟内存对应1048576个4KB的页,将这些页组织在1024个页表内,每个页表容纳1024个页.页表内每个项占4字节,存放页物理地址.故每个页表大小4KB,占据一个页.

        在处理内部,有一个控制寄存器CR3,存放着当前任务页目录的物理地址,故,又叫页目录基址寄存器.

        每个任务有自己的TSS.

        页目录,页表也是普通的页,混迹于全部的物理页中.它们和普通页的不同之处仅仅在于功能不一样.任务撤销后,它们和任务所占用的普通页一样会被回收,并分配给其他任务.

16.1.3.地址变换具体过程

        CR3寄存器给出了页目录的物理基地址;页目录给出了所有页表的物理地址,每个页表给出了它所包含的页的物理地址.

        分页机制下,之前保护模式下,段基地址+段偏移得到的是虚拟地址.要经过页部件的转换才能得到物理地址.

        处理器的页部件专门负责线性地址到物理地址的转换工作.它首先将段部件送来的32位线性地址截为3段,分别是高10位,中间10位,低12位.高10位是页目录索引,中间10位是页表的索引,低12位则作为页内偏移来用.

        注意,这种变换不是无缘无故的,而是事先安排好的.当任务加载时,操作系统先创建虚拟的段,并根据段地址的高20位决定它要用到那些页目录项和页表项.然后,寻找空闲的页,将原本应该写入段中的数据写到一个或多个页中,并将页的物理地址填写到相应的页表项中.只有这样做了,当程序运行时候,才能以相反顺序进行地址变换,找到正确的数据.

16.3使内核在分页机制下工作

16.3.1.创建内核的页目录表和页表

        先进入保护模式执行内核程序,让内核在分页机制下工作.

        准备开启页功能,首先必须创建页目录和页表.每个任务都有自己的页目录和页表.内核尽管为所有任务共有,也不例外.要想内核正常运行,必须创建它自己的页目录,页表.

        在一个理想的分页系统中,要加载程序,需先搜索可用的页,并将它们与段对应起来.这种情况下,段部件输出的线性地址和页部件输出的物理地址不同,是很自然的事. 

        因为一切发生在程序加载完毕,段和页已经有了明确的映射关系之后.这种情况下,页功能开启后,各方面能很好适应.

        内核是在开启页功能之前加载的,段在内存中的位置已经固定.这时,即便开启了页功能,线性地址也需和物理地址相同.

        注意,进入分页模式之后,所有东西的地址都成了线性地址.

        因为我们的实例内核很小,故只需对低端1M内存特殊处理,使这一部分内存的线性地址和经过页部件转换之后的物理地址相同即可.

        对于我们的实例内核,只需要一个页表就够了.

        在页目录和页表中,只保存了页表或页物理地址的高20位.这种情况下,可只关心高20位,低12位安排其他用途.

        控制寄存器CR0的最高位,也就是位31,是PG位,用于开启或关闭页功能.该位清零时,页功能被关闭,从段部件来的线性地址就是物理地址;当它置位时,页功能开启.只能在保护模式下才能开启页功能.

        开启后,段部件产生的地址,要送往页部件进行交换,以得到真正的物理地址.

16.3.2.任务全局空间和局部空间的页面映射

        每个任务有自己的页目录表,页表,当任务创建时,它们一同被创建.当任务执行时,页部件使用它们访问任务自己的私有内存空间.任务的页目录表,页表不能只包含任务的私有页面.

        任务的4GB地址空间包括两个部分:局部空间,全局空间,全局空间是所有任务共用的.内核就是所有任务共用的,它应当属于每个任务的全局空间.

        一般,公平起见,全局地址空间占据着任务4GB地址空间的高2GB,对应的线性地址范围是:0x80000000~0xFFFFFFFF;局部地址空间使用低2GB,对应的线性地址范围是:0x00000000~0x7FFFFFFF.

        在任何任务内,在任何时候,如段部件发出的线性地址高于等于0x80000000,指向和访问的就是全局地址空间,或者说内核.

        为此,要修改内核自己的页目录表,甚至是内核各个段的描述符,将内核挪到虚拟地址空间的高端,从0x80000000开始的一段连续区域.

16.4.创建内核任务 

猜你喜欢

转载自blog.csdn.net/x13262608581/article/details/125941025