动手写操作系统 -- 任务切换(独立栈)


每个task都要用到栈,下面将task栈独立开来。

注意:

在调度函数中,即这里的TickISR函数不能操作栈,否则就将任务栈弄乱了。

TickISR()函数反汇编发现函数头部有 "push si; push di;",这明显改变了栈,所以要手动加上asm {pop di; pop si;}

代码下载:funnyos_twotask_twostack.zip

调度代码如下:

void TickISR(void) {
	static int i = 0;                  //用到的变量都用static定义,放在堆中,不能使用栈
	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 taskid = 0;
	static int cnt = 0;
	static int tmp;
	
	static int task1_ss;
	static int task1_sp;
	static int task2_ss;
	static int task2_sp;
	
	asm {pop di; pop si;}
	asm cli;
	//outp(0x20,  0x20); 
	asm {
		push ax;
		push dx;
		mov dx,0x20;
		mov al,0x20;
		out dx,al;
		mov al,0x0;
		pop dx;
		pop ax;
	}
	asm sti;

	if(isTask1Start == 0) {
		isTask1Start = 1;
		asm {                           //设置任务1的栈底为0x90100
			mov tmp, ax;
			mov ax, 0x9000;
			mov ss, ax;
			mov ax, 0x100;
			mov sp, ax;
			mov ax, tmp;
		}
		testTask1();
	} 
	else if(isTask2Start == 0) {
		taskid = 1;
		asm {
			push ax; push bx; push cx; push dx; push si; push di; push bp; push ds; push es;
			push ss;
			push sp;
			pop	task1_sp;
			pop task1_ss;
		};
		task1_sp += 2;
		asm {
			mov tmp, ax;//push ax;      //设置任务1的栈底为0x80100
			mov ax, 0x8000;
			mov ss, ax;
			mov ax, 0x100;
			mov sp, ax;
			mov ax, tmp;//pop ax;
		}
		isTask2Start = 1;
		testTask2();
	} else if(isTaskAllStart == 0) {
		isTaskAllStart = 1;
		taskid = 2;
	}
	if(taskid==1) {
		taskid = 2;
		//save task1 stack
		asm {
			push ax; push bx; push cx; push dx; push si; push di; push bp; push ds; push es;
			mov task1_sp, sp;
			mov task1_ss, ss;
		};
		
		//restore task2 stack
		asm {
			mov tmp, ax;//push ax;
			mov ss, task2_ss;
			mov sp, task2_sp;
			mov ax, tmp;//push ax;
			
			pop es; pop ds; pop bp; pop di; pop si; pop dx; pop cx; pop bx; pop ax;
			iret;
		}
	} else {
		taskid = 1;
		//save task2 stack
		asm {
			push ax; push bx; push cx; push dx; push si; push di; push bp; push ds; push es;
			mov task2_sp, sp;
			mov task2_ss, ss;
		};

		//restore task1 stack
		asm {
			mov tmp, ax;//push ax;
			mov ss, task1_ss;
			mov sp, task1_sp;
			mov ax, tmp;//push ax;
			
			pop es; pop ds; pop bp; pop di; pop si; pop dx; pop cx; pop bx; pop ax;
			iret;
		}	
	}
	//asm iret;
}

每次调度,都先将之前的任务寄存器保存在任务自己的栈中,再将下个任务栈中的寄存器放置CPU中,然后iret即可继续执行下个任务。


上篇: 动手写操作系统 -- 任务切换(共享栈) 下篇: 动手写操作系统 -- 多任务调度