正式开始编写操作系统,虽然只是实现两个固定任务间的切换,但是它改变了正常C程序的工作流程,完成了无操作系统到有操作系统的跨越。
程序切换原理
函数一运行
保存函数一寄存器组
恢复函数二寄存器组
函数二运行
保存函数二寄存器组
恢复函数一寄存器组
函数一运行
上述过程就是人物切换过程,既上下文切换(context switch)。任务切换就是操作系统不停备份、恢复任务寄存器的过程。
不同的函数需要不同的栈来备份,不然函数数据就会互相破坏,在进行函数切换的时候也要做栈的切换,这里只使用MSP,但是使MSP指向不同的栈空间实现对多个栈的处理。
程序切换运行流程
1.定义TCB块
TCB是task control block的简称,意为任务控制块,与任务的相关信息会保存在TCB里。
在这里TCB保存着任务的任务栈寄存器组。既TCB里保存着Stackreg
2.定义STACKREG结构
typedef struct stackreg
{
U32 stackR4;
U32 stackR5;
U32 stackR6;
U32 stackR7;
U32 stackR8;
U32 stackR9;
U32 stackR10;
U32 stackR11;
U32 stackR12;
U32 stackSP;
U32 stackLR;
U32 stackXPSR;
}STACKREG;
保存R4-R12、SP、LR、XPSR。
3.初始化任务函数
L_TCB* LOS_TaskInit(VFUNC vfFuncPointer, U32* puiTaskStack)
{
L_TCB* pstrTcb;
STACKREG* pstrStackReg;
pstrTcb=(L_TCB*)((U32)puiTaskStack - sizeof(L_TCB));
/* */
pstrStackReg = &pstrTcb->strStackReg;
pstrStackReg->stackR4 = 0; /* R4 */
pstrStackReg->stackR5 = 0; /* R5 */
pstrStackReg->stackR6 = 0; /* R6 */
pstrStackReg->stackR7 = 0; /* R7 */
pstrStackReg->stackR8 = 0; /* R8 */
pstrStackReg->stackR9 = 0; /* R9 */
pstrStackReg->stackR10 = 0; /* R10 */
pstrStackReg->stackR11 = 0; /* R11 */
pstrStackReg->stackR12 = 0; /* R12 */
pstrStackReg->stackSP = (U32)pstrTcb;
pstrStackReg->stackLR = (U32)vfFuncPointer;
pstrStackReg->stackXPSR = MODE_USR; /* XPSR */
return pstrTcb;
}
整个函数的目的是改变puitaskstack指向地址里的值(TCB块) ,开辟pstrTcb指向puiTaskStack,pstrStackReg指向pstrTcb里的寄存器制字段,
将pstrStackReg里的所有寄存器初始化,SP存储运行时的栈地址既pstrTcb,LR保存程序的任务入口地址,XPSR存储运行状态初始为空
4.任务开始函数
此时测试函数已经初始化两个任务完毕,但是两个任务不会互相切换。使
用该函数得到下一个运行的函数的TCB从而跳转,开始任务调度
void LOS_TaskStart(void)
{
STACKREG* pstrNextTaskStackRegAddr;
/* 即将运行任务寄存器地址 */
pstrNextTaskStackRegAddr = &gpstrTask1Tcb->strStackReg;
/* 下次调度任务flag位*/
curTask = 1;
/* 切换到任务状态*/
LOS_SwitchToTask(pstrNextTaskStackRegAddr);
}
5.SwitchToTask
LOS_SwitchToTask ;恢复并运行任务的栈信息并运行
LDMIA R0!, {R4 - R12} ;
LDMIA R0, {R13} ;
ADD R0, #8 ;
LDMIA R0, {R1} ;
MSR XPSR, R1 ;
SUB R0, #4 ;
LDMIA R0, {PC} ;
ALIGN
END
运行该程序会跳转到寄存器所代表的程序段,既前面函数传入的task1的TCB中的寄存器段
6.Task1
void TEST_TestTask1(void)
{
while(1)
{
printf("task1\n");
Delay_ms(1000);
LOS_TaskSwitch();
}
}
在任务一中循环运行打印函数,打印后就进行跳转。
7.LOS_TaskSwitch
LOS_ContextSwitch
STMIA R0!, {R4 - R12}
STMIA R0!, {R13}
STMIA R0!, {R14}
MRS R2, XPSR
STMIA R0, {R2}
LDMIA R1!, {R4 - R12}
LDMIA R1, {R13}
ADD R1, #8
LDMIA R1, {R0}
MSR XPSR, R0
SUB R1, #4
LDMIA R1, {PC}
两个任务用了两个不同的栈来进行保存。
网友评论