动手写操作系统 -- 任务切换(共享栈)


定时器中断弄好了,下面就可以调度切换任务了,先用2个任务试试

还是先上图,下面是两个任务,左边是任务1,依次输出’a’,’b’,右边任务2,依次输出’1’,’2’,。。。:

pic

代码下载:funnyos_task_sharestack.7z

看下代码,分析如下:

void TickISR(void) {                 //中断处理函数,在这里调度任务
	static int i = 0;
	static int j = 0;
	static int mip_task1 = 0;
	static int mcs_task1 = 0;
	static int meflag_task1 = 0;
	static int mip_task2 = 0;
	static int mcs_task2 = 0;
	static int meflag_task2 = 0;
	static int cnt = 0;
	asm cli;
	outp(0x20,  0x20); 
	asm sti;
	
	if(isTask1Start == 0) {           //第一次中断,若任务1没运行,运行任务1
		isTask1Start = 1;
		testTask1();
	} else if(isTask2Start == 0) {    //第二次中断,若任务2没运行,
		asm {                         //先保存任务1的EFLAG,CS,IP寄存器
			push ax;
			mov ax, sp;
			add sp, 2;
			pop mip_task1;
			pop mcs_task1;
			pop meflag_task1;
			mov sp, ax;
			pop ax;
		};
		isTask2Start = 1;             //再运行任务2
		testTask2();                 
	} else if(isTaskAllStart == 0){   //第三次次中断,
		isTaskAllStart = 1;
		asm {                         //中断前运行的是任务2,保存任务2的寄存器
			push ax;
			mov ax, sp;
			add sp, 2;
			pop mip_task2;
			pop mcs_task2;
			pop meflag_task2;
			mov sp, ax;
			pop ax;
			
			push meflag_task1;        //将保存的任务1的仅存器恢复到栈上
			push mcs_task1;
			push mip_task1;
			iret;                     //iret后,将任务1的cs,ip,eflag恢复到cpu,所以下面继续执行任务1
		}
	} else {                          //第四,第五,第六。。。。次中断
		if(cnt++ % 3) {
			asm {                     //这里设置每3次调度中,2次调度任务2,1次调度任务1
				push meflag_task2;
				push mcs_task2;
				push mip_task2;
				iret;
			}
		} 
		asm {
			push meflag_task1;
			push mcs_task1;
			push mip_task1;
			iret;
		}
	
	}
	  
	//asm iret;
}

void testTask1() {                    //任务1
	static int i = 0;
	static int j = 0;
	static long x = 0;
	for(;;) {
		PC_DispChar( (i++)%35,  (j++)/35 + 1, 'a'+(j/35), DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
		for(x = 0x05fffff; x > 0; x--);   //延时
	}
}

void testTask2() {                    //任务2
	static int i = 0;
	static int j = 0;
	static long x = 0;
	for(;;) {
		PC_DispChar( (i++)%35 + 40,  (j++)/35 + 1, '0'+(j/35), DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
		for(x = 0x05fffff; x > 0; x--);   //延时
	}
}

基础知识:

在CPU触发中断前后,寄存器哪些值变了?
看下面图,这时手动执行int 08h,即软件触发定时器中断,左边是中断前的CPU值,右边是中断后的CPU值。


对比发现,中断时,CPU会自动将当前的EFLAG,下条指令对应的CS:IP压入栈中。
若中断后继续执行该任务,需将栈中的这3个寄存器保存下来。

若在某时刻切换到之前的任务,依次将上面的3个寄存器再放置栈中,执行iret指令(中断放回指令),即可继续执行任务。

注意:

上面只保存了EFLAGS,CS,IP,这样很片面,最好将CPU所有寄存器都保存下来。

而且在任务中全都用的是static变量,变量都保存在堆中,即任务没有用到自己独立的栈,共享的是系统栈。
若将任务代码中的static去掉,任务就不能正常运行了,因为栈被弄乱了。所以还需要为每个任务设置独立的栈。

上篇: 动手写操作系统 -- 定时器中断 下篇: 动手写操作系统 -- 任务切换(独立栈)