OSTaskStkInit():任务堆栈结构的初始化

2023-10-31

转载请注明出处:http://dreamlcr.cublog.cn/

----------------------------------------------------

OSTaskStkInit():任务堆栈结构的初始化

OSTaskCreate()和OSTaskCreateExt()通过调用OSTaskStkInit(),初始化任务的栈结构。因此,堆栈看起来就像中断刚发生过一样,所有寄存器都保存在堆栈中。OSTaskStkInit()的示意性代码如下所示。

OSTaskStkInit()的示意性代码L:

 

OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)

{

    模拟带参数(pdata)的函数调用; // (1)

    模拟ISR向量; // (2)

    按照预先设计的寄存器值初始化堆栈结构; // (3)

    返回栈顶指针给调用该函数的函数; // (4)

}

 

下图显示了OSTaskStkInit()在建立任务时,任务栈应该初始化成何种形式。

注意:在这里假定堆栈是从上往下递减的。下面的讨论同样适用于以相反方向从下往上递增的堆栈结构。

OSTaskStkInit():任务堆栈结构的初始化 - lanxifeng - 蓝西枫

以下的程序清单给出了OSTaskCreate(),OSTaskCreateExt()及OSTaskStkInit()的函数原型,其中参数task,pdata,ptos及opt是调用OSTaskStkInit()函数时须传递过去的参数。因为只有OSTaskCreate()函数不支持附加的opt选项,因此,当OSTaskCreate()调用OSTaskStkInit()时,将opt设置为0x0000。

程序清单:

 

INT8U OSTaskCreate(void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio);

INT8U OSTaskCreateExt(void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio, INT16U id, OS_STK *pbos, INT32U stk_size, void *pext, INT16U opt);

OS_STK *OSTaskStkInit(void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt);

 

回顾一下,任务可以是一个无限的循环,也可以在一次执行完毕后被删除掉。这里要注意的是,任务代码并不是被真正地删除了,而只是uC/OS-II不再理会该任务代码,所以该任务代码不会再运行。任务看起来与任何C函数一样,具有一个返回类型和一个参数,只是它决不返回。任务的返回类型必须定义成void型。

也就是说,在uC/OS-II中,任务是一个无限的循环,其他部分看起来与别的C函数没有什么不同。

当uC/OS-II让任务开始执行时,任务就会收到一个参数,好像是被其他任务调用了,如以下代码所列。

任务代码:

 

void MyTask(void *pdata)

{

   

    for(;;){

       

    }

}

 

如果是从其他函数中调用MyTask(),那么C编译器就会先将调用MyTask()函数的返回地址保存到堆栈中,再将参数保存到堆栈中。OSTaskStkInit()需要模仿编译器的这种动作。实际上有些编译器会将pdata参数放在一个或多个寄存器中传递,后面会讨论这类情况。

F(1)

L(1)假定pdata会被编译器保存到堆栈中,OSTaskStkInit()就会模仿编译器的这种行为,将pdata保存到堆栈中。

F(2)

L(1)与C函数的调用不一样的是,调用者的返回地址是未知的,因为该函数根本就没有被调用,只是为了建立一个任务的堆栈结构,好像这段任务代码被调用过一样。OSTaskStkInit()函数仅仅知道任务的起始地址(是作为参数传递过来的)。换言之,也不需要知道返回地址,因为任务并不再返回。

F(3)

L(2)这时,OSTaskStkInit()需将处理器的寄存器保存到堆栈中。当处理器识别并开始执行中断时,它会自动地完成该过程。一些处理器会将所有的寄存器都推入堆栈,而另一些处理器只将部分寄存器推入堆栈。一般而言,处理器至少需将程序计数器的值(中断返回地址)和处理器的状态字存入堆栈。很明显,处理器是按一定的顺序将寄存器推入堆栈的,而用户在将寄存器推入堆栈时,也就必须依照这一顺序。

F(4)

L(3)OSTaskStkInit()需要将剩下的处理器寄存器保存到堆栈中。入栈的顺序取决于处理器是否允许按不同顺序操作。有些处理器用一条指令就可以一次将全部寄存器都保存起来,有些则要几条指令。必须严格按照处理器要求的入栈顺序完成这一过程。例如,Intel 80x86有PUSHA指令,可将8个寄存器推入堆栈;对Motorola 68HC11处理器而言,在中断响应期间,所有的寄存器都会按一定顺序自动地保存到堆栈中。所以在初始化堆栈时,也必须符合这一顺序。

F(5)

L(4)在初始化堆栈以后,OSTaskStkInit()应当返回堆栈指针所指向的地址。OSTaskCreate()或OSTaskCreateExt()得到这个地址,并且保存在任务控制块中。处理器的文档应该告诉用户,堆栈指针是指向下一个可以使用的堆栈空间,还是指向上次入栈的数据。例如80x86就是指向上次存储的数据,而Motorola的68HC11处理器是指向下一个可以使用的栈空间。

现在是讨论这个问题的时候了:如果用户的C编译器用寄存器传递pdata参数,而不使用堆栈传递参数,该怎么办?请看下图。

OSTaskStkInit():任务堆栈结构的初始化 - lanxifeng - 蓝西枫

F(1)

L(1)同上一种情况类似,OSTaskStkInit()模仿调用应用程序任务代码的情况,将任务起始地址保存在堆栈中。

F(2)

L(2)同样地,OSTaskStkInit()将寄存器保存到堆栈中。当处理器识别并开始执行中断时,自动地将寄存器推入堆栈中。一些处理器会将所有的寄存器都推入堆栈,而另外一些处理器只将部分寄存器推入堆栈。一般而言,处理器至少需将程序计数器的值(中断返回地址)和处理器的状态字存入堆栈。很明显,处理器是按一定的顺序将寄存器推入堆栈的,而用户在将寄存器推入堆栈时,也必须严格依照同样的顺序。

F(3)

L(3)OSTaskStkInit()会将其余的处理器寄存器保存到堆栈中。入栈的顺序取决于处理器是否允许按不同顺序操作。有些处理器可以使用一条指令将很多寄存器推入堆栈,有些则需要几条指令。这时,应该模拟出这些指令。因为编译器是使用寄存器传递参数的(至少部分参数是通过寄存器传递的),所以应从相应文档中弄清楚pdata是通过哪个寄存器传递的,并且将该寄存器也推入堆栈。

F(4)

L(4)在初始化堆栈后,OSTaskStkInit()函数应当返回堆栈指针所指向的地址。OSTaskCreate()或OSTaskCreateExt()得到这个地址并且保存在任务控制块中。再次强调,应该清楚堆栈指针是指向下一个空的堆栈空间,还是指向上次推入堆栈的值。

总结:OSTaskStkInit()函数由OSTaskCreate()或OSTaskCreateExt()调用,需要传递的参数是任务代码的起始地址、参数指针(pdata)、任务堆栈顶端的地址和附加的opt选项,用来初始化任务的堆栈,初始状态的堆栈模拟发生一次中断后的堆栈结构。堆栈初始化工作结束后,OSTaskStkInit()返回新的堆栈栈顶指针,OSTaskCreate()或OSTaskCreateExt()将指针保存在任务的OS_TCB中。也就是说,调用OSTaskStkInit()给任务做一个初始的任务上下文堆栈。

uC/OS-II在80x86上的移植(OSTaskStkInit()部分)

下图说明了OSTaskStkInit()初始化后的堆栈内容。请注意,图中的堆栈结构不是调用OSTaskStkInit()任务之后的,而是新创建任务之后的。

OSTaskStkInit():任务堆栈结构的初始化 - lanxifeng - 蓝西枫

以下为OSTaskStkInit()的程序清单:

 

程序清单L   OSTaskStkInit()

void *OSTaskStkInit (void (*task)(void *pd), void *pdata, void *ptos, INT16U opt)

{

    INT16U *stk;

    opt = opt;

    stk = (INT16U *)ptos;

    *stk-- = (INT16U)FP_SEG(pdata);

    *stk-- = (INT16U)FP_OFF(pdata);

    *stk-- = (INT16U)FP_SEG(task);

    *stk-- = (INT16U)FP_OFF(task);

    *stk-- = (INT16U)0x0202;

    *stk-- = (INT16U)FP_SEG(task);

    *stk-- = (INT16U)FP_OFF(task);

    *stk-- = (INT16U)0xAAAA;

    *stk-- = (INT16U)0xCCCC;

    *stk-- = (INT16U)0xDDDD;

    *stk-- = (INT16U)0xBBBB;

    *stk-- = (INT16U)0x0000;

    *stk-- = (INT16U)0x1111;

    *stk-- = (INT16U)0x2222;

    *stk-- = (INT16U)0x3333;

    *stk-- = (INT16U)0x4444;

    *stk = _DS;

    return ((OS_STK *)stk);

}

 

 

L(1)

由于80x86 堆栈是16位宽的(以字为单位),OSTaskStkInit()将建立一个指向以字为单位内存区域的指针,同时要求堆栈指针指向空堆栈的顶端。

L(2)

由于笔者使用的Borland C/C++编译器配置为用堆栈而不是寄存器来传送参数pdata,所以此时参数pdata的段地址和偏移量都将被保存在堆栈中。

L(3)

堆栈中紧接着是任务函数的起始地址,理论上,此处应该为任务的返回地址,但在uC/OS-II中,任务函数必须为无限循环结构,不能有返回点。

L(4)

返回地址下面是状态字(SW),设置状态字也是为了模拟中断发生后的堆栈结构。堆栈中的SW初始化为0x0202,这将使任务启动后允许中断发生;如果设为0x0002,则任务启动后将禁止中断。需要注意的是,如果选择任务启动后允许中断发生,则所有的任务运行期间中断都允许;同样,如果选择任务启动后禁止中断,则所有的任务都禁止中断发生,而不能有所选择。

如果确实需要突破上述限制,可以通过参数pdata向任务传递希望实现的中断状态。如果某个任务选择启动后禁止中断,那么其他的任务在运行的时候需要重新开启中断。同时还要修改OS_TaskIdle()和OS_TaskStat()函数,在运行时开启中断。如果以上任何一个环节出现问题,系统就会崩溃。所以笔者还是推荐设置SW为0x0202,在任务启动时开启中断。

L(5)

堆栈中还要留出各个寄存器的空间,注意寄存器在堆栈中的位置要和运行指令PUSHA,PUSH ES和PUSH DS的压入堆栈的次序相同。上述指令在每次进入中断服务程序时都会调用。

AX,BX,CX,DX,SP,BP,SI和DI的次序是和指令PUSHA的压栈次序相同的。如果使用没有PUSHA指令的80x86处理器,就要使用多个PUSH指令压入上述寄存器,且顺序要与PUSHA相同。在程序清单中每个寄存器被初始化为不同的值,这是为了调试方便。

L(6)

Borland编译器支持伪寄存器变量操作,可以用_DS关键字取得CPU DS寄存器的值,程序清单中的标记处是用_DS直接把DS寄存器拷贝到堆栈中。

堆栈初始化工作结束后,OSTaskStkInit()返回新的堆栈栈顶指针,OSTaskCreate()或 OSTaskCreateExt()将指针保存在任务的OS_TCB中。

另外注意:OSStartHighRdy()将永远不返回到OSStart(),因为OSStartHighRdy()被OSStart()调用的函数返回地址以及相关的上下文环境都没有被保存

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OSTaskStkInit():任务堆栈结构的初始化 的相关文章

  • Lambdda表达式( Java 8 )

    在Java的语言世界里面 除了基本的数据类型 一切都是对象 例如 String字符串 每个对象实例 数组这些都是对象 在Java中方法是不能完全独立存在的 不能将方法作为参数或者返回值给实例 注意 方法 Method 函数 Function
  • SAP 下载SMW0模板文件并附加数据导入到Excel示例

    1 效果 下载模板后 导入内表数据到Excel 2 代码 关键代码DATA excel TYPE ole2 object Excel object worksheet TYPE ole2 object workbooks TYPE ole2

随机推荐

  • js获取地址栏参数function封装

    获取地址栏参数 function GetQueryString name var reg new RegExp name var r window location search substr 1 match reg if r null r
  • 《每日一题》NO.14:名词解释 后端设计篇

    芯司机 每日一题 会每天更新一道IC面试笔试题 其中有些题目已经被很多企业参考采用了哦 聪明的你快来挑战一下吧 今天是第14题 各大IC公司的笔试题经常会从名词解释开始 所以我们给大家精心准备了50个名词解释题 每天更新10个 务求将名词解
  • 2021-3-26 米斯特安全团队视频笔记二(含PHP)

    DOS命令 常用Dos命令 1 通配符 号 代替0或多个字符 号 代替一个字符 2 查看命令 3 操作命令 信息搜集 whois 备案 PHP 变量 命名格式 美元字符开头 字母 下划线 不能跟数字 特殊符号 name key 常量 通过d
  • HTML <u> 标签

    实例 使用 u 标签为文本添加下划线 p 如果文本不是超链接 就不要 u 对其使用下划线 u p 定义和用法 u 标签可定义下划线文本 浏览器支持 元素 Chrome IE Firefox Safari Opera u Yes Yes Ye
  • maven jetty 插件的使用

    jetty插件的安装 首先打开项目的pom xml文件 然后找到build节点 在其中添加plugins节点 然后再添加如下的插件
  • 清华大学uCore-操作系统内核实现

    本系列文章是学堂在线清华大学操作系统慕课 课程实验 实现uCore内核的讲义总结及个人攻略 如果有对课程感兴趣的朋友建议看原课程 前置知识 即提前要掌握的知识 C语言和汇编语言 数据结构和算法 计算机组成原理 实验内容 Lab1 uCore
  • Rust 学习笔记之内存管理与生命周期

    内存管理是理解低级语言 和硬件相关的 的基础概念 低级语言没有提供自动内存管理的解决方案 例如内置垃圾回收器 它要求程序员自己在程序中管理内存 理解内存何时何地被创建和释放可以使得程序员构建出一个高效 安全的软件 然而 低级语言的大量错误也
  • 设计模式第八讲:观察者模式和中介者模式详解

    一 观察者模式 1 背景 在现实世界中 许多对象并不是独立存在的 其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变 例如 某种商品的物价上涨时会导致部分商家高兴 而消费者伤心 还有 当我们开车到交叉路口时 遇到红灯会
  • perl中CPAN的安装

    最近一直在学习nagios监控的知识 因为使用SNMP方式进行监测 而nagios的SNMP监测文件是pl结尾的perl脚本 所以需要安装CPAN 下面就安装CPAN的安装记录步骤如下 首先安装perl 可以通过yum方式进行安装 这样减少
  • STM32F0不同代码区跳转时总失败…这些操作你做对了吗?

    STMCU官网更新了一则实战经验文件 文章以STM32F0为例 就芯片内 从BOOT区跳转到APP区 从APP区跳转到新APP区 从APP区跳回BOOT区 的跳转问题做一些交流与介绍 更多信息请前往官网详情页 文章导读 对于STM32用户
  • java 顺序结构循环队列(源代码)

    1 import java util Arrays 2 public class LoopQueue
  • python模拟登入某平台+破解验证码

    概述 python模拟登录平台 遇见验证码识别 用最简单的方法selenium da破解验证码 来自动登录平台 详细 python用selenium xpath模拟登录 破解验证码 先随便找个小说平台用户登陆 书海小说网用户登陆 书海小说网
  • Golang-指针(pointer)

    1 概念 指针 指向内存地址的变量 指针用来存储变量的内存地址 Go 语言定义变量必须声明数据类型 因为不同数据类型的数据占用不同的存储空间 导致内存地址分配大小各不相同 所有指针只能存放同一类型变量的内存地址 指针分为两种 类型指针和切片
  • Android RecyclerView实现吸顶动态效果,详细分析

    文章目录 一 ItemDecoration 二 实现RecyclerView吸顶效果 1 实现一个简单的RecyclerView 2 通过ItemDecoration画分割线 3 画出每个分组的组名 4 实现吸顶效果 完整demo 链接 h
  • Python 数组的长度

    数组 Array 是有序的元素序列 若将有限个类型相同的变量的集合命名 那么这个名称为数组名 组成数组的各个变量称为数组的分量 也称为数组的元素 有时也称为下标变量 用于区分数组的各个元素的数字编号称为下标 数组是在程序设计中 为了处理方便
  • xss-labs-master 第六关到第十关通关

    要想看前面的五关请看xss labs master 第一关到第五关通关 Level 6 进入题目废话不多说 上来就是一个test测试一下会不会变化 可以看到提示信息有输入的内容 昨天我想了一个可以看到JS变化的代码 话不多说直接上
  • Navicat 链接 MongoDB

    安装完毕后修改配置文件 vim etc mongod conf 默认127 0 0 1为只允许本地连接 0 0 0 0为不限制 多个指定服务器用 连接 bind ip 0 0 0 0 启动 mongod 启动命令行 gt systemctl
  • Android关于libs,jniLibs库的基本使用说明及冲突解决

    最近在开发中遇到了一个问题 因为项目需要集成不同的sdk 相对应的也是不同的 so文件 针对libs中 so库的引入会遇到一些问题 比如要集成第三方NDK库 如果是在eclipse中 需要放到libs下对应库的目录 如果是在Android
  • OpenCV人脸识别C++源码分析

    include cv h include highgui h include
  • OSTaskStkInit():任务堆栈结构的初始化

    转载请注明出处 http dreamlcr cublog cn OSTaskStkInit 任务堆栈结构的初始化 OSTaskCreate 和OSTaskCreateExt 通过调用OSTaskStkInit 初始化任务的栈结构 因此 堆栈