关系见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许可位图 */
------------------------------------------------------------------------------------------------