在我们的操作系统中,已经存在的进程是运行在ring1上的,它们已经不能任意地使用某些指令,不能访问某些权限更高的内存区域,但如果一项任务需要这些使用指令或者内存区域时,只能通过系统调用来实现,它是应用程序和操作系统之间的桥梁。
所以,一件事情就可能是应用程序做一部分,操作系统做一部分。这样,问题就又涉及特权级变换。
很明显,这已经难不倒我们了,因为进程的切换就是不停地在重复这么一个特权级变换的过程。在那里,触发变换的是外部中断,我们把这个诱因换一下就可以了,变成"int nnn",一切就都解决了。
前期准备:
1.准备进程代码和中断代码,系统调用代码!
2.对进程初始化代码
3.执行进程代码
一些基本概念:
结构体:是C语言中一种重要的数据类型,该数据类型由一组称为成员(或称为域,或称为元素)的不同数据组成,其中每个成员可以具有不同的类型。结构体通常用来表示类型不同但是又相关的若干数据。
进程调度代码说明:
1)PIT计数器代码:
kernel/main.c
out_byte( TIMER_MODE,RATE_GENERATOR );
out_byte( TIMER0,( u8 )( TIMER_FREQ/HZ ) );
out_byte( TIMER0,( u8 )( ( TIMER_FREQ/HZ )>>8 ) );
相关函数部分:
include/proto.h
out_byte(u16 port, u8 value)
相关变量:
include/const.h
#define TIMER0 0x40
#define TIMER_MODE 0x43
#define RATE_GENERATOR 0x34
#define TIMER_FREQ 1193182L
#define HZ 100
解说:相关变量是为了更改中断触发周期!系统中断是不停发生的
2)系统调用部分
kernel/syscall.asm
_NR_get_ticks equ 0 ; 要跟 global.c 中 sys_call_table 的定义相对应!
INT_VECTOR_SYS_CALL equ 0x90
global get_ticks ; 导出符号
bits 32
[section .text]
get_ticks:
mov eax, _NR_get_ticks
int INT_VECTOR_SYS_CALL
ret
中断门
kernel/protect.c
init_idt_desc( INT_VECTOR_SYS_CALL ,DA_386IGate,sys_call, PRIVILEGE_USER);
kernel/kernel.asm
sys_call:
call save /*调用save*/
sti /*允许中断发生*/
call [sys_call_table + eax*4] /*调用的是sys_call_table[eax]*/
mov [esi+EAXREG-P_STACKBASE],eax /*eax是把函数sys_call_table[eax]的返回值放在进程表中eax的位置,以便进程P被恢复执行时eax中装的是正确的返回值。*/
cli /*禁止中断发生*/
ret
global.c
PUBLIC system_call sys_call_table[ NR_SYS_CALL ]={ sys_get_ticks };
include/type.h
typedef void* system_call;
kernel/proc.c
PUBLIC int sys_get_ticks()
{
return ticks;
}
解说:程序调用int 90软中断,中断门调用sys_call,开中断,调用sys_call_table[eax],返回ticks,关闭中断!由于系统总是在发生,所以ticks是在不停变化的!
3)进程部分
PUBLIC void clock_handler( int irq ){
ticks++;
p_proc_ready->ticks--;
if( k_reenter != 0 ){
return;
}
if( p_proc_ready->ticks>0 ){
return;
}
schedule( );
}
延迟函数:
kernel/clock.c
PUBLIC void milli_delay( int milli_sec ){
int t = get_ticks();
while(( ( get_ticks( ) - t ) * 1000 / HZ ) < milli_sec ){
}
}
解说:如果两次获取值小于延迟值,着等待!
kernel/main.c
void TestA()
{
int i = 0;
while(1){
disp_color_str( "A.",BRIGHT|MASK_COLOR( BLACK,RED ) ); /*打印*/
milli_delay(10); /*10毫秒*/
}
}
初始化进程表:
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)p_task->initial_eip; //eip指向TestA,这表明进程将从TestA的入口地址开始运行
p_proc->regs.esp= (u32)p_task_stack; //esp指向单独的堆栈,堆栈大小为STACK_SIZE_TOTAL
p_proc->regs.eflags = 0x1202; //eflags=0x1202,恰巧设置了IF位,并把IOPL设为1,这样,进程就可以使用I/O指令,并且中断会在iretd执行时,被打开。
启动进程
p_proc_ready = proc_table;
put_irq_handler( CLOCK_IRQ,clock_handler );
enable_irq( CLOCK_IRQ );
restart();
函数部分:
lib/kliba.asm
void disp_color_str(char * info, int color);
结构体
include/proc.h
typedef struct s_stackframe {
u32 gs; /* \ */
u32 fs; /* | */
u32 es; /* | */
u32 ds; /* | */
u32 edi; /* | */
u32 esi; /* | pushed by save() */
u32 ebp; /* | */
u32 kernel_esp; /* <- 'popad' will ignore it */
u32 ebx; /* | */
u32 edx; /* | */
u32 ecx; /* | */
u32 eax; /* / */
u32 retaddr; /* return addr for kernel.asm::save() */
u32 eip; /* \ */
u32 cs; /* | */
u32 eflags; /* | pushed by CPU during interrupt */
u32 esp; /* | */
u32 ss; /* / */
}STACK_FRAME;
typedef struct s_proc {
STACK_FRAME regs;
u16 ldt_sel;
DESCRIPTOR ldts[LDT_SIZE];
int ticks ;
int priority;
u32 pid;
char p_name[16];
}PROCESS;
宏定义部分:
include/protect.h
/* 描述符索引 */
#define INDEX_DUMMY 0
#define INDEX_FLAT_C 1
#define INDEX_FLAT_RW 2
#define INDEX_VIDEO 3
#define INDEX_TSS 4
#define INDEX_LDT_FIRST 5
/* 选择子 */
#define SELECTOR_DUMMY 0
#define SELECTOR_FLAT_C 0x08
#define SELECTOR_FLAT_RW 0x10
#define SELECTOR_VIDEO (0x18+3)
#define SELECTOR_TSS 0x20
#define SELECTOR_LDT_FIRST 0x28
#define SELECTOR_KERNEL_CS SELECTOR_FLAT_C
#define SELECTOR_KERNEL_DS SELECTOR_FLAT_RW
#define SELECTOR_KERNEL_GS SELECTOR_VIDEO
/* 每个任务有一个单独的 LDT, 每个 LDT 中的描述符个数: */
#define LDT_SIZE 2
/* 选择子类型值说明 */
/* 其中, SA_ : Selector Attribute */
#define SA_RPL_MASK 0xFFFC
#define SA_RPL0 0
#define SA_RPL1 1
#define SA_RPL2 2
#define SA_RPL3 3
#define SA_TI_MASK 0xFFFB
#define SA_TIG 0
#define SA_TIL 4
/* 线性地址 → 物理地址 */
#define vir2phys(seg_base, vir) (u32)(((u32)seg_base) + (u32)(vir))
kernel/protect.c
进程LDT描述符:
init_descriptor(&gdt[INDEX_TSS],
vir2phys(seg2phys(SELECTOR_KERNEL_DS), &tss),
sizeof(tss) - 1,
DA_386TSS);
PRIVATE void init_descriptor(DESCRIPTOR *p_desc,u32 base,u32 limit,u16 attribute)
{
p_desc->limit_low = limit & 0x0FFFF;
p_desc->base_low = base & 0x0FFFF;
p_desc->base_mid = (base >> 16) & 0x0FF;
p_desc->attr1 = attribute & 0xFF;
p_desc->limit_high_attr2= ((limit>>16) & 0x0F) | (attribute>>8) & 0xF0;
p_desc->base_high = (base >> 24) & 0x0FF;
}
由段名求绝对地址
PUBLIC u32 seg2phys(u16 seg)
{
DESCRIPTOR* p_dest = &gdt[seg >> 3];
return (p_dest->base_high<<24) | (p_dest->base_mid<<16) | (p_dest->base_low);
}
/* 填充 GDT 中 TSS 这个描述符 */
memset(&tss, 0, sizeof(tss));
tss.ss0 = SELECTOR_KERNEL_DS;
init_descriptor(&gdt[INDEX_TSS], //设置填充地址位置(GDT中的LDT描述符 )
vir2phys(seg2phys(SELECTOR_KERNEL_DS), &tss), //LDT基址,这个GDT中的LDT基址指向proc_table[0].ldts
sizeof(tss) - 1, //LDT大小
DA_386TSS);
tss.iobase = sizeof(tss); /* 没有I/O许可位图 */
kernel/kernel.asm
加载ltr
xor eax, eax
mov ax, SELECTOR_TSS
ltr ax
restart:
mov esp, [p_proc_ready]
lldt [esp + P_LDT_SEL]
lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax
restart_reenter:
dec dword [k_reenter]
pop gs
pop fs
pop es
pop ds
popad
add esp, 4
iretd
随便看看
文章排行
本栏推荐
栏目更新
Copyright 2020 Inc. AllRights Reserved. Design by 操作系统设计之进程调度代码篇(1)