Liteos-A任务调度之OsTaskStackInit函数 原创
之前看过Liteos-a任务调度部分源码,本来想系统的记录一下学习的结果,但是奈何能力不足,又不甘心啥都不写,所以决定碎片化的记录一下学习到的东西。liteos-a是一个操作系统,所以看源码也需要了解基本的操作系统知识,本文不对这些基本知识进行多余的解释。如:
<font color=blue>什么是任务?任务和线程有什么区别?任务或线程都有哪些状态?
而任务调度这部分也涉及汇编语言,liteos-a是基于arm架构,所以用的是arm汇编,需要各位了解arm架构和汇编的一些知识。
任务调度包含了任务的初始化,任务的切换,而任务切换包括保存当前任务,加载新任务,当新任务运行完或运行时被更高的任务打断时,就会再次进行任务切换,即保存当前任务,加载新任务。。。这都是后话,本文讲的是任务调度之前的任务栈的初始化,具体函数是OsTaskStackInit函数。
一、代码简单注释
参数:
UINT32 taskID —— 任务ID
UINT32 stackSize —— 任务栈大小
VOID *topStack —— 任务栈顶
BOOL initFlag —— 初始化标志位,用来判断是否需要栈的初始化
返回值:
(VOID *)taskContext —— 任务上下文指针
LITE_OS_SEC_TEXT_INIT VOID *OsTaskStackInit(UINT32 taskID, UINT32 stackSize, VOID *topStack, BOOL initFlag)
{
if (initFlag == TRUE) { //判断是否进行栈的初始化
OsStackInit(topStack, stackSize); //任务栈的初始化
}
TaskContext *taskContext = (TaskContext *)(((UINTPTR)topStack + stackSize) - sizeof(TaskContext)); //声明任务上下文结构体指针,同时定位指针的位置
/* initialize the task context */
#ifdef LOSCFG_GDB //LOSCFG_GDB宏未定义,执行else
taskContext->PC = (UINTPTR)OsTaskEntrySetupLoopFrame;
#else
taskContext->PC = (UINTPTR)OsTaskEntry; //任务切换时新任务的入口函数
#endif
taskContext->LR = (UINTPTR)OsTaskExit; /* LR should be kept, to distinguish it's THUMB or ARM instruction */
taskContext->R0 = taskID; /* R0 */
#ifdef LOSCFG_THUMB //LOSCFG_THUMB宏未定义,执行else
taskContext->regCPSR = PSR_MODE_SVC_THUMB; /* CPSR (Enable IRQ and FIQ interrupts, THUMNB-mode) */
#else
taskContext->regCPSR = PSR_MODE_SVC_ARM; /* CPSR (Enable IRQ and FIQ interrupts, ARM-mode) */
#endif
//FPU(浮点单元初始化)
#if !defined(LOSCFG_ARCH_FPU_DISABLE)
/* 0xAAA0000000000000LL : float reg initialed magic word */
for (UINT32 index = 0; index < FP_REGS_NUM; index++) {
taskContext->D[index] = 0xAAA0000000000000LL + index; /* D0 - D31 */
}
taskContext->regFPSCR = 0;
taskContext->regFPEXC = FP_EN;
#endif
return (VOID *)taskContext;
}
二、任务栈和任务上下文的初始化
上面的代码总共做了两件事,一个是任务栈的初始化,一个是任务上下文结构体的初始化,其中任务栈是包含任务上下文结构体的。
1.任务栈的初始化
任务栈的初始化代码是:
VOID OsStackInit(VOID *stacktop, UINT32 stacksize)
{
/* initialize the task stack, write magic num to stack top */
(VOID)memset_s(stacktop, stacksize, (INT32)OS_STACK_INIT, stacksize); //用OS_STACK_INIT(0xCACACACA)初始化栈空间
*((UINTPTR *)stacktop) = OS_STACK_MAGIC_WORD; //用OS_STACK_MAGIC_WORD(0xCCCCCCCC)初始化栈顶指针
}
任务栈初始化完成后会有一行代码:
TaskContext *taskContext = (TaskContext *)(((UINTPTR)topStack + stackSize) - sizeof(TaskContext));
声明一个任务上下文结构体,并指向任务栈中的某一位置,空间分布大概是这样:
这个栈空间我是以栈底作为高地址,栈顶作为低地址,栈空间初始化完成了,下面就该初始化任务上下文结构体了。
2.任务上下文结构体初始化
首先是任务上下文结构体代码:
/* The size of this structure must be smaller than or equal to the size specified by OS_TSK_STACK_ALIGN (16 bytes). */
typedef struct {
#if !defined(LOSCFG_ARCH_FPU_DISABLE)
UINT64 D[FP_REGS_NUM]; /* D0-D31 */
UINT32 regFPSCR; /* FPSCR */
UINT32 regFPEXC; /* FPEXC */
#endif
UINT32 R4;
UINT32 R5;
UINT32 R6;
UINT32 R7;
UINT32 R8;
UINT32 R9;
UINT32 R10;
UINT32 R11;
/* It has the same structure as IrqContext */
UINT32 reserved2; /**< Multiplexing registers, used in interrupts and system calls but with different meanings */
UINT32 reserved1; /**< Multiplexing registers, used in interrupts and system calls but with different meanings */
UINT32 USP; /**< User mode sp register */
UINT32 ULR; /**< User mode lr register */
UINT32 R0;
UINT32 R1;
UINT32 R2;
UINT32 R3;
UINT32 R12;
UINT32 LR;
UINT32 PC;
UINT32 regCPSR;
} TaskContext;
结构体中包括ARM的R0~R12通用寄存器,PC、LR、CPSR以及32个64位浮点寄存器,还有FPSCR和FPEXC寄存器,这些都是ARM硬件上结构,还有reserved1、reserved2,这两个成员是保留的,USP和ULR是任务调度时非特权模式下要用到,这是后话。
根据OsTaskStackInit函数的代码,在任务栈初始化时主要初始化了PC、LR、R0、CPSR、32个64位浮点寄存器、FPSR和FPEXC这几个任务上下文结构体成员,这是加载任务并成功运行所必须的,所以必须初始化。
一个任务被创建并初始化后就可以参加调度了,创建任务和任务调度部分有机会的话会继续写,而任务栈的初始化现在就到此为止了,再写就不礼貌了。。
欢迎访问我的CSDN博客:请添加链接描述