Directory:

TaskContext defines the context structure

2. Task stack related functions

3. The task enters the exit function

4, summary

Continue to analyze hongmeng light kernel source code, we begin to analyze the task and task scheduling module. First, let’s introduce the basic concept of task stack. A task stack is a descending stack that grows from a high address to a low address, and the stack pointer points to the location of the element to be pushed. The unused stack space is initialized to 0xCACACACA represented by the OS_TASK_STACK_INIT macro, and the top of the stack is initialized to 0xCCCCCCCC represented by the OS_TASK_MAGIC_WORD macro. A schematic diagram of a task stack is shown below, where the bottom pointer is the largest memory address of the stack, and the top pointer is the smallest memory address of the stack, and the stack pointer grows from the bottom of the stack to the top.

Task Context is another important concept of Task and Task scheduling module. It refers to the running environment of a Task, such as program counter, stack pointer, general purpose register and so on. Task Context Switching is the core of multi-task scheduling, which is the basis for multiple tasks running on the same CPU core. During the task scheduling, the register information used by the exiting task is saved to the task stack, and the context information is read from the stack of the entering task to recover the register information.

Below, we analyze the task stack, task stack initialization source code, if it involves the development board part, to the development board project Targets \ Cortex-m7_nucleo_F767zi_gcc \ for example for source analysis. First, take a look at the task context structure.

TaskContext defines the context structure

In the kernel\arch\arm\cortex-m7\ GCC \los_arch_context.h file, the context structure is defined as follows, mainly floating point register, general purpose register.

typedef struct TagTskContext {
#if ((defined(__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \
     (defined(__FPU_USED) && (__FPU_USED == 1U)))
    UINT32 S16;
    UINT32 S17;
    UINT32 S18;
    UINT32 S19;
    UINT32 S20;
    UINT32 S21;
    UINT32 S22;
    UINT32 S23;
    UINT32 S24;
    UINT32 S25;
    UINT32 S26;
    UINT32 S27;
    UINT32 S28;
    UINT32 S29;
    UINT32 S30;
    UINT32 S31;
#endif
    UINT32 uwR4;
    UINT32 uwR5;
    UINT32 uwR6;
    UINT32 uwR7;
    UINT32 uwR8;
    UINT32 uwR9;
    UINT32 uwR10;
    UINT32 uwR11;
    UINT32 uwPriMask;
    UINT32 uwR0;
    UINT32 uwR1;
    UINT32 uwR2;
    UINT32 uwR3;
    UINT32 uwR12;
    UINT32 uwLR;
    UINT32 uwPC;
    UINT32 uwxPSR;
#if ((defined(__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \
     (defined(__FPU_USED) && (__FPU_USED == 1U)))
    UINT32 S0;
    UINT32 S1;
    UINT32 S2;
    UINT32 S3;
    UINT32 S4;
    UINT32 S5;
    UINT32 S6;
    UINT32 S7;
    UINT32 S8;
    UINT32 S9;
    UINT32 S10;
    UINT32 S11;
    UINT32 S12;
    UINT32 S13;
    UINT32 S14;
    UINT32 S15;
    UINT32 FPSCR;
    UINT32 NO_NAME;
#endif
} TaskContext;
Copy the code

2. Task stack related functions

2.1 Task stack initialization function

The stack initialization function VOID *HalTskStackInit(t() is defined in the kernel\arch\arm\cortex-m7\ GCC \los_context.c file. This function is called by the function UINT32 OsNewTaskInit() in the kernel/SRC/LOS_task.c file to initialize the task. It is also called by the task creation function UINT32 LOS_TaskCreateOnly() to initialize the stack of the newly created task.

The function uses three parameters: task number UINT32 taskID, stackSize UINT32 stackSize, and VOID *topStack. (1) initialize the stack to OS_TASK_STACK_INIT, (2) initialize the top of the stack to OS_TASK_MAGIC_WORD.

TaskContext *context For newly created tasks, starting at the bottom of the stack, a stack sizeof sizeof(TaskContext) holds the context’s data. (4) If floating point calculation is supported, we need to initialize the floating point related registers. ⑸ initialize the universal register, where. UwLR is initialized as (UINT32)(UINTPTR)HalSysExit. UwPC initialized to (UINT32)(UINTPTR)OsTaskEntry, which is the position of the first instruction run by the CPU when it performs the task for the first time. These two functions will be examined below.

VOID * taskContext = VOID * taskContext = VOID * taskContext = VOID * taskContext = VOID * taskContext = VOID * taskContext = VOID * taskContext = VOID * taskContext = VOID * taskContext On the stack, the first member of the context structure is stored in the direction from the TaskContext *context pointer increment, followed by the second member… In addition, when initializing the stack, except for a few special registers, the initial values of different registers are meaningless, but there are also some initialization rules. For example, the R2 register is initialized to 0x02020202L, and the R12 register is initialized to 0x121212L. The initialization content is related to the register number, and the rest is similar.

LITE_OS_SEC_TEXT_INIT VOID *HalTskStackInit(UINT32 taskID, UINT32 stackSize, VOID *topStack) { TaskContext *context = NULL; errno_t result; /* Initialize the task stack, write magic num to stack top */ ⑴ result = memset_s(topStack, stackSize, (INT32)(OS_TASK_STACK_INIT & 0xFF), stackSize); if (result ! = EOK) { printf("memset_s is failed:%s[%d]\r\n", __FUNCTION__, __LINE__); } 2 * ((UINT32 *) (topStack)) = OS_TASK_MAGIC_WORD; TaskContext = (UINTPTR)topStack + stackSize) -sizeof (TaskContext)); # if ((defined (__FPU_PRESENT) && (__FPU_PRESENT u) = = 1) && \ (defined (__FPU_USED) && (__FPU_USED = = 1 u))) (4) the context - > S16 = 0xAA000010; context->S17 = 0xAA000011; context->S18 = 0xAA000012; context->S19 = 0xAA000013; context->S20 = 0xAA000014; context->S21 = 0xAA000015; context->S22 = 0xAA000016; context->S23 = 0xAA000017; context->S24 = 0xAA000018; context->S25 = 0xAA000019; context->S26 = 0xAA00001A; context->S27 = 0xAA00001B; context->S28 = 0xAA00001C; context->S29 = 0xAA00001D; context->S30 = 0xAA00001E; context->S31 = 0xAA00001F; context->S0 = 0xAA000000; context->S1 = 0xAA000001; context->S2 = 0xAA000002; context->S3 = 0xAA000003; context->S4 = 0xAA000004; context->S5 = 0xAA000005; context->S6 = 0xAA000006; context->S7 = 0xAA000007; context->S8 = 0xAA000008; context->S9 = 0xAA000009; context->S10 = 0xAA00000A; context->S11 = 0xAA00000B; context->S12 = 0xAA00000C; context->S13 = 0xAA00000D; context->S14 = 0xAA00000E; context->S15 = 0xAA00000F; context->FPSCR = 0x00000000; context->NO_NAME = 0xAA000011; #endif ⑸ context->uwR4 = 0x04040404L; context->uwR5 = 0x05050505L; context->uwR6 = 0x06060606L; context->uwR7 = 0x07070707L; context->uwR8 = 0x08080808L; context->uwR9 = 0x09090909L; context->uwR10 = 0x10101010L; context->uwR11 = 0x11111111L; context->uwPriMask = 0; context->uwR0 = taskID; context->uwR1 = 0x01010101L; context->uwR2 = 0x02020202L; context->uwR3 = 0x03030303L; context->uwR12 = 0x12121212L; context->uwLR = (UINT32)(UINTPTR)HalSysExit; context->uwPC = (UINT32)(UINTPTR)OsTaskEntry; context->uwxPSR = 0x01000000L; [6] return (VOID *) context; }Copy the code

2.2 Obtaining the task stack waterline function

The current stack size may not be the maximum as tasks are loaded and unloaded. UINT32 OsGetTaskWaterLine(UINT32 taskID) Indicates the maximum stack size that can be obtained, that is, WaterLine. The function is defined in the kernel\ SRC \ LOs_task. c file. It takes one parameter, that is, UINT32 taskID task NUMBER. The returned value UINT32 peakUsed indicates the obtained waterline value, that is, the maximum number used by the task stack.

OS_TASK_STACK_INIT specifies the unused space on the stack. If the top of the stack is equal to the magic word, the stack is not overrun. A temporary stack pointer, stackPtr, is used to determine whether the stack is being used by incrementing the variables toward the bottom of the stack. The while loop ends and the stack pointer, stackPtr, points to the largest unused stack address. The code at (2) gets the maximum amount of used stack space, the required waterline. OS_NULL_INT = null;

This function is called by the kernel\base\los_task.c function LOS_TaskInfoGet(UINT32 taskId, TSK_INFO_S *taskInfo) to obtain task information. Shell modules also use come or stack information.

UINT32 OsStackWaterLineGet(const UINTPTR *stackBottom, const UINTPTR *stackTop, UINT32 *peakUsed) { UINT32 size; const UINTPTR *tmp = NULL; (1) (1) {1 = 1; while ((tmp < stackBottom) && (*tmp == OS_STACK_INIT)) { tmp++; } ⑵ size = (UINT32)(UINTPTR)stackBottom - (UINTPTR) TMP); *peakUsed = (size == 0) ? size : (size + sizeof(CHAR *)); return LOS_OK; } else { *peakUsed = OS_INVALID_WATERLINE; return LOS_NOK; } } UINT32 OsGetTaskWaterLine(UINT32 taskID) { UINT32 *stackPtr = NULL; UINT32 peakUsed; ⑴ If (*(UINT32 *)(UINTPTR)OS_TCB_FROM_TID(taskID)->topOfStack == OS_TASK_MAGIC_WORD) {stackPtr = (UINT32 *)(UINTPTR)(OS_TCB_FROM_TID(taskID)->topOfStack + OS_TASK_STACK_TOP_OFFSET); while ((stackPtr < (UINT32 *)(OS_TCB_FROM_TID(taskID)->stackPointer)) && (*stackPtr == OS_TASK_STACK_INIT)) { stackPtr + = 1; } ⑵ peakUsed = OS_TCB_FROM_TID(taskID)->stackSize - ((UINT32)(UINTPTR)stackPtr ->topOfStack); } else {⑶ PRINT_ERR("CURRENT task %s stack overflow! \n", OS_TCB_FROM_TID(taskID)->taskName); peakUsed = OS_NULL_INT; } return peakUsed; }Copy the code

3. The task enters the exit function

3.1. Task exit function

When the context is initialized, the link register is set to the function (UINT32)(UINTPTR)HalSysExit, which is defined in the file kernel\ SRC \ LOs_task.c. Call LOS_IntLock() in the function code to close the interrupt, and then enter an infinite loop. This function is theoretically not executed during normal task scheduling. This function is also called when an active call to LOS_Panic() C triggers an exception in the event of a system exception.

LITE_OS_SEC_TEXT_MINOR VOID HalSysExit(VOID)
{
    LOS_IntLock();
    while (1) {
    }
}
Copy the code

3.2. The task enters the function

VOID OsTaskEntry(UINT32 taskId) is a function defined in kernel\base\los_task.c. Then execute the entry function (2) that calls the task. After the task is completed, execute ⑶ deleting the task. Generally, the task entry execution function is a while loop. When the task is not executed, it will be scheduled to other tasks or idle tasks, and will not be executed to the task deletion stage.

LITE_OS_SEC_TEXT_INIT VOID OsTaskEntry(UINT32 taskID) { UINT32 retVal; ⑴ LosTaskCB *taskCB = OS_TCB_FROM_TID(taskID); 2 (VOID) taskCB - > taskEntry (taskCB - > arg); (3) retVal = LOS_TaskDelete (taskCB - > taskID); if (retVal ! = LOS_OK) { PRINT_ERR("Delete Task[TID: %d] Failed! \n", taskCB->taskID); }}Copy the code

summary

This article led us to study the basic concepts of task stack and task context of Hongmeng Light kernel, and analyzed the code of task stack initialization. The follow-up will also launch more articles to share, please look forward to, but also welcome to share learning, using hongmeng light kernel experience, any questions, suggestions, you can leave a message to us: gitee.com/openharmony… . To make it easier to find the light kernel repository, visit gitee.com/openharmony… “, follow Watch, like Star, and Fork to your account, thank you.

Author: zhushangyuan_

For more information, visit Harmonyos.51cto.com, a collaboration between 51CTO and Huawei