μC/OS-Ⅱ中的任务
μC/OS-Ⅱ操作系统内核的主要工作就是对任务的调度和管理。
1 任务的基本概念
任务及其内存结构
将大任务分解为多个小任务,可以使系统并发地运行多个任务,提高处理器的利用率,加快程序的执行速度,现代操作系统大多是多任务操作系统。
在μC/OS-Ⅱ中,小任务对应的程序实体为“任务”,μC/OS-Ⅱ是能对小任务进行管理和调度的多任务操作系统。
从代码上来看,μC/OS-Ⅱ的任务是一个函数;
从存储结构上看,μC/OS-Ⅱ由三部分组成:
任务程序代码(函数): 任务的执行部分
任务堆栈:保存任务的工作变量
任务控制块:记录任务的各个操作
进程:具有私有空间的任务
线程:没有私有空间的任务
μC/OS-Ⅱ中的任务属于线程。
μC/OS-Ⅱ中的任务(最多64个):用户任务和系统任务
用户任务:由应用程序设计者编写
系统任务:由系统提供
2 任务的状态
μC/OS-Ⅱ任务的5钟状态:睡眠、就绪、运行、等待和中断服务
睡眠:以代码的形式留在ROM或RAM中,没有被配置任务控制块或被剥夺了任务控制块。
就绪:配备了任务控制块并在任务就绪表中就绪登记,具备运行的充分条件。
运行:处于就绪状态,经调制器判断获得了CPU的使用权。
等待:正在运行的任务,等待一段时间或等待一个事件发生后再运行。
中断服务:正在运行的任务响应中断申请中止去执行中断服务程序。
3 用户任务代码的一般结构
用户应用程序的一般结构
用户任务不是由主函数调用的函数,在系统中它与主函数处于平等地位,主函数负责创建任务并将它们交给系统,由系统决定何时被运行和被中止。
OS_TASKCreate()是创建任务的函数,OSStart()是启动μC/OS-Ⅱ的函数。
4 系统任务
系统任务:空闲任务和统计任务
空闲任务(每个应用程序必须使用):
统计任务(根据要求选择使用):
每秒计算一次CPU在单位时间内被使用的时间,计算结果以百分比的形式存放在变量OSCPUsage中。
若要使用统计任务,须在系统头文件OS_CFG中的系统配置常数OS_TASK_STAT_EN置为1,在程序中要调用函数OSStartInit()进行初始化。
5 任务的优先权及优先级别
系统总是把最低优先级别OS_LOWEST_PRIO自动赋值给空闲任务。
每个任务都具有唯一的优先级别,也是任务在系统中的标识。
6 任务堆栈
堆栈:在存储器中按数据“后进先出(LIFO)”的原则组织的连续存储空间,每个任务都有自己的堆栈,在任务控制块中都有一个指向任务堆栈的指针。
1 任务堆栈的创建
typedef unsigned int OS_STK;
#define TASK_STK_SIZE 512
OS_STK TaskStk[TASK_STK_SIZE];
OSTaskcreate(void (
*task)(void *pd), //指向任务的指针
void *pdata, //传递给任务的参数
OS_STK *ptos,//任务堆栈栈顶指针
INTBU prio//指定任务优先级的参数
);
堆栈的增长方向是由系统所使用的处理器决定的。
头文件OS_CFG.H中的OS_STK_GROWTH作为选择开关
#if OS_STK_GROWTH == 1//向下增长
OSTaskcreate(MyTask, &MyTaskAgu, &MyTaskStk[MyTaskStkN - 1], 20);
#else//向上增长
OSTaskcreate(MyTask, &MyTaskAgu, &MyTaskStk[0], 20);
#endif
2 任务堆栈的初始化à将任务初始数据存放到任务堆栈
OS_STK *OSTaskStkInit(void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt);
该函数由OSTaskcreate()函数来调用。
7 任务控制块及其链表
μC/OS-Ⅱ把系统所有任务的额控制块链接为两条链表。
任务控制块链表
一条空任务链表,一条任务块链表。
空任务块链表:系统调用OSInit()对μC/OS-Ⅱ进行初始化时,在RAM中建立一个OS_TCB结构的数组OSTCBTbl[],各个元素链接成一个链表。
空任务链表的元素数目:
OS_CFG.H:OS_MAX_TASKS用户任务的最大数目
UCOSII.H:OS_N_SYS_TASKS系统任务的数目(空闲任务+统计任务(1 or 2))
任务控制块链表创建为双向链表,可加快任务控制块的访问速度。
当前任务控制块:正在占有CPU且处于运行状态的任务所属的控制块,是μC/OS-Ⅱ访问频度最高的控制块。
任务控制块的初始化
INTBU OSTCBInit(INTBU prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT16U stk_size, void *pext, INT16U opt);
Ø 为被创建任务从空任务控制块链表获取一个任务控制块
Ø 用任务的属性对任务控制块各个成员进行赋值
Ø 把这个任务控制块链入到任务控制块链表
8 任务就绪表及任务调度
为系统中处于就绪状态的任务分配CPU是多任务操作系统的核心工作。
判断哪些任务处于就绪状态;进行任务调度。
对任务就绪表的操作
登记、注销和从就绪表的就绪任务中得知具有最高优先级任务的标识。
1. 登记
当某个任务处于就绪状态时,系统将该任务登记在就绪表中,在就绪表对应位置置1.
OSRdyGrp |= OSMapTbl[prio >> 3];
OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
利用该数组可以加快运算速度。
2. 注销
当某个任务脱离就绪任务时,系统在就绪表中将该任务对应位置置0.
if((OSRdyTbl[prio >> 3] & -OSMapTbl[prio & 0x07]) == 0)
OSRdyGrp &= -OSMapTbl[prio >> 3];
3. 最高优先级就绪任务的查找
y = OSUnMapTal[OSRdyGrp];//获取优先级别的D5、D4、D3位
x = OSUnMapTal[OSRdyTbl[y]];//获取优先级别的D2、D1、D0位
prio = (y << 3) + x;//获取就绪任务的优先级别
或
y = OSUnMapTbl[OSRdyGrp];
prio = (INTBU) ((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
4. 任务调度à每时每刻让优先级最高的就绪任务处于运行状态
任务切换:令CPU中止当前正在运行的任务转而去运行另一个任务
任务的调度:按某种规则进行任务切换
1. 调度器的主要共作
任务调度器:在就绪表中查找优先级别最高的就绪任务;
实现任务的切换:获取待运行任务的TCB指针、进行断点数据切换
任务级调度器:OSSched()
中断级调度器:OSIntExt()
2. 获得待运行就绪任务控制块的指针
操作系统通过任务控制块TCB来管理任务,被中止任务的任务控制块指针存放在全局变量OSTCBCur中,所以调度器主要获取待运行就绪任务控制块的指针。
通过OSSchedLock()和OSSchedUnlock()函数给调度器上锁和解锁,上锁一次,变量OSLockNexting加1;解锁一次,变量OSLockNexting减1。
3. 任务切换宏OS_TASK_SW()
断点:任务被中止运行时的位置
断点数据:当时存放在CPU的PC、PSW和通用寄存器等各寄存器中的数据
一个被中止的任务能否正确地在断点处恢复运行,关键在于能否正确地在CPU各寄存器中恢复断点数据;能否正确恢复断点数据的关键在于CPU的堆栈指针SP是否有正确的指向。
任务的切换就是断点数据的切换,断点数据的切换也就是CPU堆栈指针的切换。
OSCtxSW()的工作:
Ø 断点指针保存到任务堆栈
Ø CPU通用寄存器的内容保存到任务堆栈
Ø 被中止任务的任务堆栈指针当前值保存到该任务的任务控制块的OSTCBStkPtr中
Ø 获得待运行任务的任务控制块
Ø CPU获得待运行任务的任务堆栈指针
Ø 将待运行任务堆栈中通用寄存器的内容恢复到CPU通用寄存器中
Ø 使CPU获得待运行任务的断点指针
对于被中止任务:把任务的断点指针压入任务堆栈
对于待运行任务:把任务堆栈里上次任务被中止时存放在堆栈中的中断指针推入PC寄存器。
通过改变PC值的指令实现出栈和入栈。
9 任务的创建
1 用函数OSTaskCreate()创建任务
2 用函数OSTaskcreateExt()创建任务
3 创建任务的一般方法
在调用OSStart()之前,必须已经创建了至少一个任务,不允许在中断服务程序中创建任务。
PC功能函数提供了三类服务:字符显示、运行时间测量和其他服务
10. 任务的挂起和恢复
1 挂起任务
挂起任务à停止这个任务的运行
OSTaskSuspend()挂起ßàOSTaskResume()恢复
挂起任务:
INTBU OSTaskSuspend(INTBU prio);
挂起自身时,参数为OS_PRIO_SELF
2 恢复任务
恢复任务
INTBU OSTaskResume(INTBU prio);
11.其他任务管理函数
1 任务优先级别的修改
可通过函数OSTaskChangePrio()来改变任务的优先级别
2 任务的删除à将任务置于睡眠状态
#if OS_TASK_DEL_EN
INTBU OSTaskDel(INTBU prio);
删除自身时,参数为OS_PRIO_SELF
μC/OS-Ⅱ利用被删除任务的任务控制块成员OSTCBDelReq作为请求删除方的被删除方的联络信号,同时提供了一个函数OSTaskDelReq(),双方都能访问OSTCBDelReq信号,从而根据信号的状态决定各自的行为。
INTBU OSTaskDelReq(INTBU prio);
被删除任务调用这个函数时,参数为OS_PRIO_SELF
3 查询任务的信息
OSTaskQuery(INTBU prio, OS_TCB *pdata);
12 μC/OS-Ⅱ的初始化和任务的启动
1 μC/OS-Ⅱ的初始化
使用OSInit()初始化μC/OS-Ⅱ自身的运行环境,将对所有全局变量和数据结构进行初始化,并创建空闲任务OSTaskIdle,并赋之以最低的优先级别和永远的就绪状态。
主要是创建包括空任务控制块链表在内的5个空数据缓冲区,还要创建一个数组OSTCBPrioTbl[OS_LOWEST_PRIO + 1](按任务的优先级别的顺序把任务控制块的指针存放在对应的元素中)
2 μC/OS-Ⅱ的启动
μC/OS-Ⅱ进行任务的管理从调用启动函数OSStart()开始,调用函数前至少创建了一个用户任务。
小结
n 任务由任务控制块、任务堆栈和任务代码三部分组成。
Ø 任务控制块:感知和控制任务
Ø 任务堆栈:保护和恢复断点
Ø 任务代码:超循环结构,描述任务的执行过程
n 创建任务时,OSTaskcreate()和OSTaskcreateExt()用来给任务分配任务控制块和任务堆栈。
n 系统是按任务就绪表和任务的优先级别来调度任务的。
n 任务切换的核心工作是任务堆栈指针的切换
n 任务的优先级别也是任务的标识
n 应用程序首先调用OSInit()函数对全局变量和数据结构进行初始化,以及建立μC/OS-Ⅱ的运行环境。
应用程序通过调用OSSart() 函数进入多任务管理,在调用之前至少创建一个任务。