#define ADR_GDT 0x00270000 //全局描述符表的开始位置
#define AR_TSS32 0x0089
struct SEGMENT_DESCRIPTOR *gdt=(struct SEGMENT_DESCRIPTOR *) ADR_GDT;
set_segmdesc(gdt+3,103,(int) &tss_a,AR_TSS32); //第一个进程,这个地方gdt+3其实是bootpack.c的程序的位置
set_segmdesc(gdt+4,103,(int) &tss_b,AR_TSS32); //这个是第二个进程,tss_a和tss_b可以先想成程序地址
TSS 全称为task state segment,是指在操作系统进程管理的过程中,进程切换时的任务现场信息。下面一段对TSS的介绍摘自http://blog.csdn.net/goodlixueyong/article/details/6018281 :
X86体系从硬件上支持任务间的切换。为此目的,它增设了一个新段:任务状态段(TSS),它和数据段、代码段一样也是一种段,记录了任务的状态信息。
与其它段一样,TSS也有描述它的结构:TSS描述符表,它记录了一个TSS的信息,同时还有一个TR寄存器,它指向当前任务的TSS。任务切换的时候,CPU会将原寄存器的内容写出到相应的TSS,同时将新TSS的内容填到寄存器中,这样就实现了任务的切换。
TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指挂起当前正在执行的任务,恢复或启动执行另一个任务。Linux任务切换是通过switch_to这个宏来实现的,它利用长跳指令,当长跳指令的操作数是TSS描述符的时候,就会引起CPU的任务的切换,此时,CPU将所有寄存器的状态保存到当前任务寄存器TR所指向的TSS段中,然后利用长跳指令的操作数(TSS描述符)找到新任务的TSS段,并将其中的内容填写到各个寄存器中,最后,将新任务的TSS选择符更新到TR中。这样系统就开始运行新切换的任务了。由此可见,通过在TSS中保存任务现场各寄存器状态的完整映象,实现了任务的切换。 task_struct中的tss成员就是记录TSS段内容的。当进程被切换前,该进程用tss_struct保存处理器的所有寄存器的当前值。当进程重新执行时,CPU利用tss恢复寄存器状态。
上面提到的TR寄存器是用来记住当前正在运行的任务,当任务切换时,TR的值会自动变化。我们可以通过LTR汇编指令来向TR寄存器写入值。
TSS的结构:
struct TSS32
{
int backlink,esp0,ss0,esp1,ss1,esp2,ss2,cr3;
int eip,eflags,eax,ecx,edx,ebx,esp,ebp,esi,edi;
int es,cs,ss,ds,fs,gs;
int ldtr,iomap;
};
设置到全局描述符中的tss_a和tss_b还没有初始化。因为上述的进程一其实就是第一个开始的进程,因此tss_a在切换的时候会自动保存数据到tss_a,因此不用初始化;现在对tss_b进行初始化,
tss_b_esp=(int) memman_alloc_4k(memman,64*1024)+64*1024-8;
tss.esp=task_b_esp;
tss.eip=(int) &task_b_main;
tss.cs=2*8;
tss.ds=1*8;
tss.ss=1*8;
tss.gs=1*8;
tss.fs=1*8;
tss.es=1*8;
重点是esp,eip和cs寄存器,因为这几个寄存器关乎到程序的执行,因为第二个进程的代码写在bootpack.c中,二bootpack.c的代码段是在GDT中的第三段,因此CS为2*8,段内偏移就是进程二的地址&task_b_main。这样就会切换到第二个进程执行了。
那么前面说的CPU会以极短的时间来轮流执行这些进程,这个结合定时器实现的,每个进程运行到定时器的一定时间,就自动切换到其他进程,这样每个进程执行一段时间。
上面只是本人看完《30天自制操作系统》和查阅一些资料的一些总结,其中有很多也是模模糊糊,在这只是理一下思路。