第3章第2节 任意任务间的切换
来源:互联网 发布:top域名有什么用 编辑:程序博客网 时间:2024/05/23 13:15
目前更新到5.3节,请在http://dl.dbank.com/c02ackpwp6下载5.3节的全部文档
本节源代码请在http://dl.dbank.com/c04m68jx5a下载
第2节 任意任务间的切换
上一节我们使用2个固定的任务验证了操作系统任务切换的功能,但这些代码并不具有通用性,如果要扩充其它任务,就必须修改操作系统函数,这显然是不可接受的。操作系统作为独立于用户代码的部分,它的内部细节应该是不被用户所见的,是一个黑盒,需要做到用户只需要修改操作系统提供的接口文件里面的参数,调用接口函数就可以完全满足程序开发的要求。因此,在本节我们将对上节的代码做些改动,使其可以支持任意多个任务之间的互相切换,而又不需要修改Wanlix目录下的操作系统代码,仅仅是编写srccode目录下的用户代码,调用操作系统的接口函数即可,这样才真正实现了操作系统的独立性。
首先我们来看看任务切换函数——WLX_TaskSwitch。上节中,这个函数固定在两个任务之间切换,因此要实现可以切换到任何一个函数的功能就必须修改此函数,需要为这个函数增加一个入口参数,用这个入口参数来指明需要切换到的任务。WLX_TaskSwitch函数的主要功能是做好任务切换前的准备,将当前运行任务的栈指针和将要运行任务的栈指针存入到对应的全局变量中,上节中,为每个任务分别指定了guiTask21CurSp和guiTask2CurSp全局变量保存它们的当前栈指针,每个全局变量绑定到了任务,因此,这个入口参数还必须能够关联到任务的栈指针。
为此,我们引入TCB的概念,在操作系统里这是一个非常重要的概念。TCB是Task
typedef
{
}W_TCB;
TCB结构不只是这么简单,只是到目前为止就是这么简单,随着操作系统功能的不断完善,TCB也会不断的增加它的结构。
我们可以考虑将TCB放到任务的栈中。当任务创建时在栈的开始处保留一块内存作为TCB的存放空间,TCB之后的栈空间才作为真正的栈使用,这样,任务的TCB也就与任务绑定到了一起,每个TCB就可以代表一个任务。由于ARM芯片是线性地址空间,也就是说每个内存地址都是唯一的,因此每个任务堆栈的开始地址也就是唯一的,因此每个TCB的地址也就是唯一的了,这样我们就可以使用TCB的地址来代表各个不同的任务。
图
有了TCB,下面我们来修改WLX_TaskSwitch函数。修改很简单,只是将TCB指针作为入口参数,在函数里替换掉原来与任务相关的全局变量,修改后的函数如下:
00107
00108
00109
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00107行,入口参数pstrTcb是将要运行任务的TCB指针,准备切换到该任务运行。
00111行,将当前运行任务的TCB中保存当前栈指针变量的地址存入全局变量gpuiCurTaskSpAddr中。
00114行,将将要运行任务的TCB中保存当前栈指针的变量,也就是当前的栈指针,存入全局变量guiNextTaskSp中。
00117行,将全局变量gpstrCurTcb
00119行,调用汇编函数WLX_ContextSwitch执行具体的寄存器备份、恢复操作。
同理,WLX_TaskStart函数也需要做类似的变量替换,不再介绍,读者自行分析。
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
由于增加了TCB,因此必须修改任务初始化函数。从本节开始,所有的任务将采用WLX_TaskCreate函数创建,在WLX_TaskCreate函数内分别对TCB和栈进行初始化。
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00018行,函数返回值是被创建任务的TCB指针;入口参数vfFuncPointer是创建任务所使用的函数;入口参数pucTaskStack是创建任务所使用的栈地址,是栈的低地址;入口参数uiStackSize是栈的大小。
00023行,对入口参数判断,如果函数指针为空,则返回NULL空指针代表创建任务失败。在C语言里,指针为NULL(也就是0)代表无效指针,因为0地址的内存一般都是中断向量表的复位向量,正常使用指针时是不会指向这里的。
00030行,对入口参数判断,如果栈指针为NULL或者栈大小为0,则返回失败。
00037行,调用WLX_TaskTcbInit函数初始化任务的TCB,并得到当前任务的TCB指针。
00040行,调用WLX_TaskStackInit函数初始化当前的任务栈。
00042行,任务创建成功,返回当前任务的TCB。以后就可以使用这个返回值来代表这个任务了。
目前的TCB比较简单,只有一个保存栈地址的变量,在WLX_TaskTcbInit函数里就是来初始化这个变量的,并返回TCB指针。
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00051行,函数返回值是被创建任务的TCB指针,创建任务后这个TCB就代表该任务;pucTaskStack是任务的栈地址,是栈的低地址;uiStackSize是栈的大小。
00057行,确定栈顶地址。“(U32)pucTaskStack
00060行,确定TCB地址。“(U32)ucStackBy4
00063行,初始化TCB中的变量,保存任务的栈指针。
00065行,返回任务的TCB指针。
TCB的引入也需要对WLX_TaskStackInit函数做简单的修改,由于该函数只是简单使用TCB替换了原来专用的全局变量,没有大的改动,就不做过多介绍了,读者可以对比上节的函数自己分析。
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
经过上述修改操作系统就具有通用性了,无论建立多少个任务都无需修改操作系统的代码,只要为任务分配一个栈空间,使用WLX_TaskCreate函数就可以创建任务,并可以使用这个任务的TCB指针作为入口参数,调用WLX_TaskSwitch函数就可以切换到这个任务。
另外说一点,创建任务时,需要用户先用全局变量为所创建的任务申请一个任务栈空间,将它的起始地址和大小作为参数传递给WLX_TaskCreate函数来创建任务。如果能将申请任务栈的操作封装到WLX_TaskCreate函数里面就会更方便一些,但我在GNU环境下没有找到配置堆(heap)的方法(谁知道请在论坛上反馈一下,谢谢!),因此无法在WLX_TaskCreate函数里使用C函数库里的malloc函数从堆中申请任务栈。如果使用自己编写的堆函数则不如C库函数的方便,兼容性也不好,因此这里需要用户自己申请任务的栈空间。后面在Cortex内核芯片上,我们将换一个编译器,到那时候再完善这个功能。
在看最终效果前,我们再对任务切换过程中寄存器备份、恢复操作做最后一点优化。上节我们使用WLX_ContextSwitch函数完成任务寄存器入栈、出栈及最后跳转的操作,这样做存在2个问题:
1.
2.
为此,我们将原有的WLX_ContextSwitch函数拆分成2个函数,WLX_ContextSwitch函数和WLX_SwitchToTask函数。WLX_ContextSwitch函数仍被WLX_TaskSwitch函数调用,用于每次任务切换,WLX_SwitchToTask函数被WLX_TaskStart函数调用,用于第一次任务切换。
这两个汇编函数没有实质性的改动,不再详细介绍,请读者自行分析。
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
至此就完成了本节代码的修改。在测试代码里我们建立3个任务,TEST_TestTask1、TEST_TestTask2和TEST_TestTask3,并将它们的TCB保存到全局变量gpstrTask1Tcb、gpstrTask1Tcb
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
这3个任务在循环运行,向串口打印数据,每间隔一段时间切换到另外一个任务继续运行。TEST_TestTask1任务运行时向串口打印“Task1
编译本节代码,串口打印如下图:
图
- 第3章第2节 任意任务间的切换
- 第4章第4节 任务切换钩子函数
- 第3章第1节 两个固定任务之间的切…
- 第4章第6节 任务自结束
- 第3章第3节 用户代码入口——根任务…
- 第1章第2节 操作系统的分类
- 第4章第5节 任务创建和任务删除钩…
- 第2章第3节 ARM7芯片的函数调用标…
- JAVA--第四周实验--任务2--求任意整数降序数的程序。(编程思想)
- 第2章第2节 ARM7汇编语言简介
- 第3章第5节 发布Wanlix操作系统
- 第4章第3节 实时事件触发的实时抢…
- 第4章第3节 实时事件触发的实时抢…
- 第4章第3节 实时事件触发的实时抢…
- 第2章第4节 Wanlix的文件组织结构
- 第2章第5节 Wanlix的开发环境
- 第4章第2节 定时器触发的实时抢占…
- 第4章第2节 定时器触发的实时抢占…
- 第2章第4节 Wanlix的文件组织结构
- 第2章第5节 Wanlix的开发环境
- 第3章 Wanlix操作系统
- 第3章第1节 两个固定任务之间的切…
- spring mvc(注解)上传文件的简单例子
- 第3章第2节 任意任务间的切换
- 第3章第3节 用户代码入口——根任务…
- 我的学习之路_第二十二章_事务
- 第3章第4节 使用Wanlix编写交通红…
- Makefile编写及一个简单的Makefile架构实现
- 第3章第5节 发布Wanlix操作系统
- 第4章 Mindows操作系统
- 第4章第1节 Mindows的文件组织结构
- 第4章第2节 定时器触发的实时抢占…