多任务是指一个单处理器同时执行多个不同任务。实际上这样的说法并不完全准确,因为一个单处理器在同一个时刻只能执行一个任务,但它可以在很短的时间内在多个任务之间切换,如每秒在多个任务之间切换100次,它们在同一时刻并不是并行的,但在使用者看来这些任务好似在同时运行的。我们先来看一下CPU在执行两个任务时的运行过程:
可以看到:任务A和任务B在小时间片中是串行的,而在大时间片中是并行的。在前10ms中CPU在执行任务A,在10-20ms时,CPU在执行任务B;而在大的时间片中,如前60ms或更大的时间片如1s中里来看任务A和任务B就是并行的。
我们知道,每个任务在执行时都需要使用CPU中的寄存器,那么当每个任务执行时,CPU中的这些寄存器都必须是当前任务所正确使用的。而CPU在多个任务之间切换时就需要将这些任务所使用的寄存器的值做一些特殊的处理:当CPU从任务A切换到任务B时,需要将任务A所使用的所有寄存器的值保留下来,放入内存。然后将B所使用的寄存器的值由内存装入CPU的各个寄存器。当CPU从任务B再切换加任务A时,CPU又要从内存中装入任务原先使用的CPU寄存器的值到CPU寄存器中。
比如在10ms时刻,任务A所使用%eax寄存器的值为0x1111,%ebx的值为0x2222。此时CPU需要由任务A切换到任务B,在CPU切换任务之前需要将任务A所使用的寄存器的值保留到内存中,再切换到任务B,并将任务B之前所使用的CPU寄存器的值装入CPU寄存器中。在执行任务B的过程中,时间到了20ms,此时%eax寄存器的值为0x3333,%ebx寄存器的值为0x4444,也就是说这是任务B所使用的两个寄存器的值。此时CPU需要由任务A切换到任务B,在切换之前先要将任务B所使用的寄存器的值保留到内存中,再切换到任务B,并将之前任务A所使用的寄存器的值重新装入CPU寄存器中,恢复%eax和%ebx的值为0x1111和0x2222并以此方式继续……
当一个程序在执行的时CPU想要将其切换为其它程序之前,先要将它的相关信息和所有寄存器的信息保存到内存当中,并把将要切换成当前执行程序的相关信息和所有寄存器的信息由内存装入,再切换到新的程序中执行。CPU使用任务状态TSS(Task State Segment)来存储这些信息,TSS有104个字节,并且在GDT中有一个描述符指向这个TSS。当CPU进行任务切换时,CPU把任务信息自动的存储在TSS当中。TSS格式如下:
CPU进行任务切换时可以使用call [TSS selector], [offset]和jmp call [TSS selector], [offset]来切换至一个任务。也就是载入这个TSS的内容到CPU的寄存器中并执行%cs:%eip所指向的程序代码并执行。call指令的操作符为TSS所在的GDT全局描述符。先来看一下指向TSS的全局描述符:
也就是说,我们有一个GDT描述符,里面存放的是TSS的地址,比如说这个GDT的选择子为0x20那么在切换任务时就要执行call $0x20, $0。事实上,操作系统内核的任务切换过程很复杂,并采用复杂任务调度算法。在本小节中主要是针对TSS任务切换来学习CPU的多任务机制,所以采用了只有两个简单任务切换的办法,关于任务调度算法我们会在后续章节中学习。
对于每一个任务来说,它们都可以独立的使用CPU中的寄存器。为了与内核程序区分,我们要将这些普通程序运行的权限设置为3,也就是用户权限。这些程序的内存寻址方式与前面讲的内核程序寻址方式一致,但它们使用的不是GDT全局描述符,而是LDT局部描述符。其实LDT与GDT的本质是完全一样的,只不过LDT是为普通任务所用。在一个任务的TSS中有一个字段为LDT,这里存放的是一个GDT描述符,这个GDT描述符描述了这个任务的LDT描述符所在的内存地址,而这个LDT描述符描述了这个任务可用的内存段区域:代码段和数据段。
对于LDT它的内容与GDT基本上是一样的,只是DLP字段为3而不是像GDT那样为0。使用LDT时的选择子RPL是3而不是0。下图为LDT的选择子:
TI字段的值如果是0则代表是GDT选择子,如果是1代表是LDT选择子。另外,由于RPL为3,所以对于LDT的选择子为0x7、0xf、0x17、0x1f等等。
Copyright © 2015-2023 问渠网 辽ICP备15013245号