进程的进程表、进程体、GDT、TSS的关系,以及进程表的初始化

关系见P252,图6-9

进程表与GDT的关系:

进程表里的LDT Selector对应GDT中的一个描述符,而这个描述符所指向的内存空间就存在与进程表内。

进程表与进程体的关系:

进程表是进程的描述,进程运行过程中如果被中断,各个寄存器的值都会被保存进进程表中。使用到进程表堆栈。

但是,在我们的第一个进程开始前并不需要初始化太多内容,只需知道进程的入口地址就足够了。同时需要设置esp,指向进程表。

GDT与TSS的关系:

GDT中需要有一个描述符来对应TSS,需要事先初始化这个描述符。

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

进程结构体,结构体PROCESS   proc.h

typedef  struct s_proc{

   STACK_FRAME    regs;                //寄存器

   t_16                  ldt_ sel ;           //LDT选择子

   DESCRIPTOR      ldts[LDT_SIZE]  //LDTs

   t_32                   pid                   //进程号

   char                    P_name[16]     //名字

}PROCESS;

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

进程结构体有了,下面我们在global.c中声明一个进程表

PUBLIC PROCESS   proc_table[NR_TASKS]

好了,进程表有了下面我们来初始化它。当NR_TASKS=1时,就相当于定义了一个proc_table.

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

进程表初始化:main.c

PROCESS* p_proc=proc_table;

p_proc--->ldt_sel=SELECTOR_LDT_FIRST;                                              

  //设置进程表中进程的ldt_sel,ldt_sel被赋值SELECTOR_LDT_FIRST,这个宏的定义在代码6-1-中

memcpy(&p_proc->ldts[0], &gdt[SELECTOR_KERNEL_CS >> 3], sizeof(DESCRIPTOR));  //将SELECTOR_KERNEL_CS所指的描述符拷贝到进程PCB的ldts[0]处
p_proc->ldts[0].attr1 = DA_C | PRIVILEGE_TASK << 5;                              // change the DPL
memcpy(&p_proc->ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3], sizeof(DESCRIPTOR)); //将SELECTOR_KERNEL_DS所指的描述符拷贝到进程PCB的ldts[1]处
p_proc->ldts[1].attr1 = DA_DRW | PRIVILEGE_TASK << 5;                          // change the DPL

//LDT中共有两个描述符,分别被初始化为内核代码段和内核数据段,只是改变了一下DPL,以让其运行在低特权级下(2)
p_proc->regs.cs  = ((8 * 0) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //8*0和8*1是选择子,而且是进程PCB中的LDT描述符的选择子。

//cs指向LDT中的第一个描述符 (3) 通过cs中的这个描述符跳转到内核代码段                              //LDT选择子是从0开始的。一个描述符相隔8个字节,所以要乘以8
p_proc->regs.ds  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //根据选择子的属性确定是全局选择子还是LDT选择子。 
p_proc->regs.es  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //本例会根据cs中的选择子自动到进程PCB中读取LDT描述符
p_proc->regs.fs  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;    //本例比较特殊的地方是进程LDT描述符在进程PCB中,没有固定的LDT段
p_proc->regs.ss  = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | RPL_TASK;

//ds,es,fs,ss指向LDT中的第二个描述符
p_proc->regs.gs  = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | RPL_TASK;

//gs指向显存,只是其RPL发生改变
p_proc->regs.eip = (u32)TestA;

//eip指向TestA,这表明进程将从TestA的入口地址开始运行
p_proc->regs.esp = (u32) task_stack + STACK_SIZE_TOTAL;

//esp指向单独的堆栈,堆栈大小为STACK_SIZE_TOTAL
p_proc->regs.eflags = 0x1202; // IF=1, IOPL=1, bit 2 is always 1.

//eflags=0x1202,恰巧设置了IF位,并把IOPL设为1,这样,进程就可以使用I/O指令,并且中断会在iretd执行时,被打开。

//代码中使用到的宏,基本在protect.h中

p_proc_ready = proc_table;

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

这里要记得把LDT跟GDT是联系在一起的,别忘了填充GDT中进程的LDT的描述符protect.c

 init_descriptor(

   &gdt[INDEX_LDT_FIRST],                                                           //设置填充地址位置(GDT中的LDT描述符 ) 
   vir2phys(seg2phys(SELECTOR_KERNEL_DS),  proc_table[0].ldts),  //LDT基址,这个GDT中的LDT基址指向proc_table[0].ldts       (1)
   LDT_SIZE * sizeof(DESCRIPTOR) - 1,                                           //LDT大小

   DA_LDT);                                                                                   //LDT属性

   seg2phys(SELECTOR_KERNEL_DS)把段地址转换为物理地址,把DS右移几位。

   vir2phys为宏定义,在protect.h

   #define vir2phys(seg_base, vir)      (u32)(((u32)seg_base) + (u32)(vir))

 

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

GDT与TSS 

会发现没有初始化的只有TSS了,在protect.c中的init_Prot()中初始化TSS以及对应的描述符。

memset(&tss, 0, sizeof(tss));
 tss.ss0  = SELECTOR_KERNEL_DS;
 init_descriptor(&gdt[INDEX_TSS],
   vir2phys(seg2phys(SELECTOR_KERNEL_DS), &tss),
   sizeof(tss) - 1,
   DA_386TSS);
 tss.iobase = sizeof(tss); /* 没有I/O许可位图 */

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

posted on 2011-05-20 08:29  wanghj_dz  阅读(3330)  评论(0编辑  收藏  举报

导航