摘要:LiteOS任务栈是高地址向低地址生长的递减栈,栈指针指向即将入栈的元素位置。

我们介绍下LiteOS任务栈的基础概念。LiteOS任务栈是高地址向低地址生长的递减栈,栈指针指向即将入栈的元素位置。初始化后未使用过的栈空间初始化的内容为宏OS_STACK_INIT代表的数值0xCACACACA,栈顶初始化为宏OS_STACK_MAGIC_WORD代表的数值0xCCCCCCCC。一个任务栈的示意图如下,其中,栈底指针是栈的最大的内存地址,栈顶指针,是栈的最小的内存地址,栈指针从栈底向栈顶方向生长。

1、 LOS_StackInfo任务栈结构体定义

typedef struct {
VOID *stackTop; // 栈顶指针
UINT32 stackSize; // 栈大小
CHAR *stackName; // 栈名称
} StackInfo;

另外定义了一个宏函数OS_STACK_MAGIC_CHECK(topstack)用于检测栈是否有效,当栈顶等于OS_STACK_MAGIC_WORD栈是正常的,没有溢出,否则栈顶被改写,发生栈溢出。

/* 1:有效正常的栈 0:无效,发生溢出的栈 */
#define OS_STACK_MAGIC_CHECK(topstack) (*(UINTPTR *)(topstack) == OS_STACK_MAGIC_WORD)

2、 LOS_StackInfo任务栈支持的操作

2.1 任务栈初始化

栈初始化函数VOID OsStackInit()使用2个参数,一个是栈顶指针VOID *stacktop,一个是初始化的栈的大小。把栈内容初始化为OS_STACK_INIT,把栈顶初始化为OS_STACK_MAGIC_WORD。

该函数被arch\arm\cortex_m\src\task.c的*OsTaskStackInit(UINT32 taskId, UINT32 stackSize, VOID *topStack)方法调用,进一步被创建任务时的OsTaskCreateOnly()方法调用,完成新创建任务的任务栈初始化。

VOID OsStackInit(VOID *stacktop, UINT32 stacksize)
{
(VOID)memset_s(stacktop, stacksize, (INT32)OS_STACK_INIT, stacksize);
*((UINTPTR *)stacktop) = OS_STACK_MAGIC_WORD;
}

2.2 获取任务栈水线

随着任务栈入栈、出栈,当前栈使用的大小不一定是最大值,OsStackWaterLineGet()可以获取的栈使用的最大值即水线WaterLine。

该函数需要3个参数,UINTPTR *stackBottom是栈底指针,const UINTPTR *stackTop栈顶指针,UINT32 *peakUsed用于返回获取的水线值,即任务栈使用的最大值。

⑴处代码表示如果*stackTop == OS_STACK_MAGIC_WORD,说明栈没有被溢出破坏,从栈顶开始栈内容被写满宏OS_STACK_INIT的部分是没有使用过的栈空间。使用tmp指针变量依次向栈底方向增加,判断栈是否被使用过,while循环结束,栈指针tmp指向最大的未使用过的栈地址。⑵处代码使用栈底指针stackBottom减去tmp,获取最大的使用过的栈空间大小,即需要的水线。⑶处如果栈顶溢出,则返回无效值OS_INVALID_WATERLINE。

该函数被kernel\base\los_task.c中的函数LOS_TaskInfoGet(UINT32 taskId, TSK_INFO_S *taskInfo)调用,获取任务的信息。在shell模块也会使用来或者栈信息。

UINT32 OsStackWaterLineGet(const UINTPTR *stackBottom, const UINTPTR *stackTop, UINT32 *peakUsed)
{
UINT32 size;
const UINTPTR *tmp = NULL;
⑴ if (*stackTop == OS_STACK_MAGIC_WORD) {
tmp = stackTop + 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;
}
}

3、 LOS_Task任务栈初始化

我们以AArch32 Cortex-M核为例,剖析下任务栈初始化的过程,相关代码分布在arch\arm\cortex_m\include\arch\task.h、arch\arm\cortex_m\src\task.c。首先看下任务上下文。

3.1 TaskContext上下文结构体定义

任务上下文(Task Context)指的是任务运行的环境,例如包括程序计数器、堆栈指针、通用寄存器等内容。在多任务调度中,任务上下文切换(Task Context Switching)属于核心内容,是多个任务运行在同一CPU核上的基础。LiteOS内核中,上下文的结构体定义如下:

typedef struct tagContext {
#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 R4;
UINT32 R5;
UINT32 R6;
UINT32 R7;
UINT32 R8;
UINT32 R9;
UINT32 R10;
UINT32 R11;
UINT32 PriMask;
UINT32 R0;
UINT32 R1;
UINT32 R2;
UINT32 R3;
UINT32 R12;
UINT32 LR;
UINT32 PC;
UINT32 xPSR;
#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;

3.2 LOS_Task任务栈初始化

上文中提到在创建任务的时候,会使用VOID *OsTaskStackInit()函数初始化任务栈。我们分析下函数代码,它需要3个参数,UINT32 taskId待创建任务的编号,UINT32 stackSize任务栈的大小,VOID *topStack任务栈的栈顶指针。

⑴处代码调用OsStackInit()函数初始化栈,初始化栈内容和栈顶为魔术字。⑵处代码获取任务上下文的指针地址TaskContext *taskContext,栈的底部大小为sizeof(TaskContext)的栈空间存放上下文的数据。⑶处如果支持浮点数计算,需要初始化浮点数相关的寄存器。⑷初始化通用寄存器,其中LR初始化为(UINT32)OsTaskExit,PC初始化为(UINT32)OsTaskEntry,CPU首次执行该任务时运行的第一条指令的位置,这2个函数下文会分析。⑸处返回值是指针(VOID *)taskContext,这个就是任务初始化后的栈指针,注意不是从栈底开始了,栈底保存的是上下文,栈指针要减去上下文占用的栈大小。

在栈中,从TaskContext *taskContext指针增加的方向,依次保存上下文结构体的第一个成员,第二个成员…另外,初始化栈的时候,除了特殊的几个寄存器,不同寄存器的初始值没有什么意义,也有些初始化的规律。比如R2寄存器初始化为0x02020202L,R12寄存器初始化为0x12121212L初始化的内容和寄存器编号有关联,其余类似。

LITE_OS_SEC_TEXT_INIT VOID *OsTaskStackInit(UINT32 taskId, UINT32 stackSize, VOID *topStack)
{
TaskContext *taskContext = NULL; ⑴ OsStackInit(topStack, stackSize);
⑵ taskContext = (TaskContext *)(((UINTPTR)topStack + stackSize) - sizeof(TaskContext)); #if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \
⑶ (defined (__FPU_USED) && (__FPU_USED == 1U)))
taskContext->S16 = 0xAA000010;
taskContext->S17 = 0xAA000011;
taskContext->S18 = 0xAA000012;
taskContext->S19 = 0xAA000013;
taskContext->S20 = 0xAA000014;
taskContext->S21 = 0xAA000015;
taskContext->S22 = 0xAA000016;
taskContext->S23 = 0xAA000017;
taskContext->S24 = 0xAA000018;
taskContext->S25 = 0xAA000019;
taskContext->S26 = 0xAA00001A;
taskContext->S27 = 0xAA00001B;
taskContext->S28 = 0xAA00001C;
taskContext->S29 = 0xAA00001D;
taskContext->S30 = 0xAA00001E;
taskContext->S31 = 0xAA00001F;
taskContext->S0 = 0xAA000000;
taskContext->S1 = 0xAA000001;
taskContext->S2 = 0xAA000002;
taskContext->S3 = 0xAA000003;
taskContext->S4 = 0xAA000004;
taskContext->S5 = 0xAA000005;
taskContext->S6 = 0xAA000006;
taskContext->S7 = 0xAA000007;
taskContext->S8 = 0xAA000008;
taskContext->S9 = 0xAA000009;
taskContext->S10 = 0xAA00000A;
taskContext->S11 = 0xAA00000B;
taskContext->S12 = 0xAA00000C;
taskContext->S13 = 0xAA00000D;
taskContext->S14 = 0xAA00000E;
taskContext->S15 = 0xAA00000F;
taskContext->FPSCR = 0x00000000;
taskContext->NO_NAME = 0xAA000011;
#endif ⑷ taskContext->R4 = 0x04040404L;
taskContext->R5 = 0x05050505L;
taskContext->R6 = 0x06060606L;
taskContext->R7 = 0x07070707L;
taskContext->R8 = 0x08080808L;
taskContext->R9 = 0x09090909L;
taskContext->R10 = 0x10101010L;
taskContext->R11 = 0x11111111L;
taskContext->PriMask = 0;
taskContext->R0 = taskId;
taskContext->R1 = 0x01010101L;
taskContext->R2 = 0x02020202L;
taskContext->R3 = 0x03030303L;
taskContext->R12 = 0x12121212L;
taskContext->LR = (UINT32)OsTaskExit;
taskContext->PC = (UINT32)OsTaskEntry;
taskContext->xPSR = 0x01000000L; ⑸ return (VOID *)taskContext;
}

3.3 OsTaskExit()函数

在初始化上下文的时候,链接寄存器设置的是函数VOID OsTaskExit(VOID),该函数定义在文件arch\arm\cortex_m\src\task.c。函数代码里调用__disable_irq()关中断,然后进入死循环。该函数理论上不会被执行,忽略即可。

LITE_OS_SEC_TEXT_MINOR VOID OsTaskExit(VOID)
{
__disable_irq();
while (1) { }
}

3.4 LOS_Task任务进入函数

在初始化上下文的时候,PC寄存器设置的是函数VOID OsTaskEntry(UINT32 taskId),该函数定义在文件kernel\base\los_task.c,我们来分析下源代码,⑴处释放任务的自旋锁,开中断。然后执行⑵处代码获取taskCB,并调用任务的入口函数。等任务执行完毕后,检查taskCB->taskFlags是否设置为自删除标记OS_TASK_FLAG_DETACHED,如果是则删除任务。

LITE_OS_SEC_TEXT_INIT VOID OsTaskEntry(UINT32 taskId)
{
LosTaskCB *taskCB = NULL;
VOID *ret = NULL; LOS_ASSERT(OS_TSK_GET_INDEX(taskId) < g_taskMaxNum); ⑴ LOS_SpinUnlock(&g_taskSpin);
(VOID)LOS_IntUnLock(); ⑵ taskCB = OS_TCB_FROM_TID(taskId); #ifdef LOSCFG_OBSOLETE_API
ret = taskCB->taskEntry(taskCB->args[0], taskCB->args[1], taskCB->args[2],
taskCB->args[3]); /* 0~3: just for args array index */
#else
ret = taskCB->taskEntry(taskCB->args);
#endif ⑶ if (OsTaskDeleteCheckDetached(taskCB)) {
OsTaskDeleteDetached(taskCB);
} else {
OsTaskDeleteJoined(taskCB, ret);
}
}

本文分享自华为云社区《LiteOS内核源码分析系列六 -任务及调度(2)-任务LOS_Task》,原文作者:zhushy 。

点击关注,第一时间了解华为云新鲜技术~

LiteOS内核源码分析:任务栈信息的更多相关文章

  1. LiteOS内核源码分析:任务LOS_Schedule

    摘要:调度,Schedule也称为Dispatch,是操作系统的一个重要模块,它负责选择系统要处理的下一个任务.调度模块需要协调处于就绪状态的任务对资源的竞争,按优先级策略从就绪队列中获取高优先级的任 ...

  2. 鸿蒙内核源码分析(用栈方式篇) | 程序运行场地谁提供的 | 百篇博客分析OpenHarmony源码 | v20.04

    百篇博客系列篇.本篇为: v20.xx 鸿蒙内核源码分析(用栈方式篇) | 程序运行场地谁提供的 | 51.c.h .o 精读内核源码就绕不过汇编语言,鸿蒙内核有6个汇编文件,读不懂它们就真的很难理解 ...

  3. 鸿蒙内核源码分析(编译过程篇) | 简单案例窥视GCC编译全过程 | 百篇博客分析OpenHarmony源码| v57.01

    百篇博客系列篇.本篇为: v57.xx 鸿蒙内核源码分析(编译过程篇) | 简单案例窥视编译全过程 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...

  4. 鸿蒙内核源码分析(双向链表篇) | 谁是内核最重要结构体? | 开篇致敬鸿蒙内核开发者 | v1.11

    子曰:"见贤思齐焉,见不贤而内自省也."<论语>:里仁篇 百篇博客系列篇.本篇为: v01.xx 鸿蒙内核源码分析(双向链表篇) | 谁是内核最重要结构体 | 51.c ...

  5. 鸿蒙内核源码分析(信号消费篇) | 谁让CPU连续四次换栈运行 | 百篇博客分析OpenHarmony源码 | v49.04

    百篇博客系列篇.本篇为: v49.xx 鸿蒙内核源码分析(信号消费篇) | 谁让CPU连续四次换栈运行 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁 ...

  6. 鸿蒙内核源码分析(任务管理篇) | 任务池是如何管理的 | 百篇博客分析OpenHarmony源码 | v5.05

    百篇博客系列篇.本篇为: v05.xx 鸿蒙内核源码分析(任务管理篇) | 任务池是如何管理的 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...

  7. 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 百篇博客分析OpenHarmony源码 | v4.05

    百篇博客系列篇.本篇为: v04.xx 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度 ...

  8. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)

    http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...

  9. Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】

    原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.c ...

  10. 鸿蒙内核源码分析(根文件系统) | 先挂到`/`上的文件系统 | 百篇博客分析OpenHarmony源码 | v66.01

    百篇博客系列篇.本篇为: v66.xx 鸿蒙内核源码分析(根文件系统) | 先挂到/上的文件系统 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...

随机推荐

  1. 全局关闭Unity编译的CS警告

    实现方式 Editor和Game的全局CSharp编译配置文件名: Assets/mcs.rsp 添加如下内容可屏蔽对应的警告信息 -nowarn:1234 常用内容 CS0219 未使用的publi ...

  2. C# WebBrowser document.execCommand()解析

            //         // Summary:         //     对文档执行指定的命令.         //         // Parameters:         ...

  3. Ubuntu16.04 设置jar开机自启脚本

    1.编写脚本 后缀  ".sh" #! /bin/sh ### BEGIN INIT INFO # Provides: start-adb-connect-manager # Re ...

  4. Linux MIPI 摄像头驱动框架编写(RN6752解码芯片)

    一.概述 在编写 MIPI 摄像头驱动之前,需要先了解 Media 子系统的链路关系,这里我就不介绍了,需要的看我之前的笔记:Linux Media 子系统链路分析. 理解 Media 子系统链路关系 ...

  5. CSS 也能实现 if 判断?实现动态高度下的不同样式展现

    最近在群里,有个小伙伴问了这么一道很有趣的问题: CSS 能否实现,容器再某个高度下是某种表现,一旦超出某个高度,则额外展示另外一些内容 为了简化实际效果,我们看这么一张示意效果图: 可以看到,当容器 ...

  6. BI 数据可视化平台建设(2)—筛选器组件升级实践

    作者:vivo 互联网大数据团队-Wang Lei 本文是vivo互联网大数据团队<BI数据可视化平台建设>系列文章第2篇 -筛选器组件. 本文主要介绍了BI数据可视化平台建设中比较核心的 ...

  7. Log4j入门使用

    前言 本篇文章主要在于,初步了解log4j,以及对它的简单使用 欢迎点赞 收藏 留言评论 私信必回哟 博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言 @ 目录 一,log4j简介 二,配 ...

  8. C语言编写两个函数,分别求两个整数的最大公约数和最小公倍数,并用主函数调用这两个函数,然后输出结果。两个整数由键盘输入。约定最大公约数为正整数,两数里有负的则最小公倍数就为负的。

    /* 开发者:慢蜗牛 开发时间:2020.5.28 程序功能:计算最大公约数和最小公倍数 */ #include<stdio.h> #include<math.h> int m ...

  9. 自学day7 数组

    typora-copy-images-to: media 数组 一.概念 对象中可以通过键值对存储多个数据,且数据的类型是没有限制的,所以通常会存储一个商品的信息或一个人的信息: var obj = ...

  10. css零散笔记——修改input样式input:-internal-autofill-selected背景色

    闲聊: 小颖项目中的登录页需将  input  背景色设为透明,小颖将 input  的背景色设置后,发现表单自动填充后还是会有背景色,然后发现 浏览器 自带了背景色: 效果图:           ...