正式開(kāi)始編寫(xiě)操作系統(tǒng),雖然只是實(shí)現(xiàn)兩個(gè)固定任務(wù)間的切換,但是它改變了正常C程序的工作流程,完成了無(wú)操作系統(tǒng)到有操作系統(tǒng)的跨越。
程序切換原理
函數(shù)一運(yùn)行
保存函數(shù)一寄存器組
恢復(fù)函數(shù)二寄存器組
函數(shù)二運(yùn)行
保存函數(shù)二寄存器組
恢復(fù)函數(shù)一寄存器組
函數(shù)一運(yùn)行
上述過(guò)程就是人物切換過(guò)程,既上下文切換(context switch)。任務(wù)切換就是操作系統(tǒng)不停備份、恢復(fù)任務(wù)寄存器的過(guò)程。
不同的函數(shù)需要不同的棧來(lái)備份,不然函數(shù)數(shù)據(jù)就會(huì)互相破壞,在進(jìn)行函數(shù)切換的時(shí)候也要做棧的切換,這里只使用MSP,但是使MSP指向不同的棧空間實(shí)現(xiàn)對(duì)多個(gè)棧的處理。
程序切換運(yùn)行流程
1.定義TCB塊
TCB是task control block的簡(jiǎn)稱(chēng),意為任務(wù)控制塊,與任務(wù)的相關(guān)信息會(huì)保存在TCB里。
在這里TCB保存著任務(wù)的任務(wù)棧寄存器組。既TCB里保存著Stackreg
2.定義STACKREG結(jié)構(gòu)
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.初始化任務(wù)函數(shù)
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;
}
整個(gè)函數(shù)的目的是改變puitaskstack指向地址里的值(TCB塊) ,開(kāi)辟pstrTcb指向puiTaskStack,pstrStackReg指向pstrTcb里的寄存器制字段,
將pstrStackReg里的所有寄存器初始化,SP存儲(chǔ)運(yùn)行時(shí)的棧地址既pstrTcb,LR保存程序的任務(wù)入口地址,XPSR存儲(chǔ)運(yùn)行狀態(tài)初始為空
4.任務(wù)開(kāi)始函數(shù)
此時(shí)測(cè)試函數(shù)已經(jīng)初始化兩個(gè)任務(wù)完畢,但是兩個(gè)任務(wù)不會(huì)互相切換。使
用該函數(shù)得到下一個(gè)運(yùn)行的函數(shù)的TCB從而跳轉(zhuǎn),開(kāi)始任務(wù)調(diào)度
void LOS_TaskStart(void)
{
STACKREG* pstrNextTaskStackRegAddr;
/* 即將運(yùn)行任務(wù)寄存器地址 */
pstrNextTaskStackRegAddr = &gpstrTask1Tcb->strStackReg;
/* 下次調(diào)度任務(wù)flag位*/
curTask = 1;
/* 切換到任務(wù)狀態(tài)*/
LOS_SwitchToTask(pstrNextTaskStackRegAddr);
}
5.SwitchToTask
LOS_SwitchToTask ;恢復(fù)并運(yùn)行任務(wù)的棧信息并運(yùn)行
LDMIA R0!, {R4 - R12} ;
LDMIA R0, {R13} ;
ADD R0, #8 ;
LDMIA R0, {R1} ;
MSR XPSR, R1 ;
SUB R0, #4 ;
LDMIA R0, {PC} ;
ALIGN
END
運(yùn)行該程序會(huì)跳轉(zhuǎn)到寄存器所代表的程序段,既前面函數(shù)傳入的task1的TCB中的寄存器段
6.Task1
void TEST_TestTask1(void)
{
while(1)
{
printf("task1\n");
Delay_ms(1000);
LOS_TaskSwitch();
}
}
在任務(wù)一中循環(huán)運(yùn)行打印函數(shù),打印后就進(jìn)行跳轉(zhuǎn)。
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}
兩個(gè)任務(wù)用了兩個(gè)不同的棧來(lái)進(jìn)行保存。