UcOS-II任务管理.

Slides:



Advertisements
Similar presentations
3 的倍数的特征 的倍数有 : 。 5 的倍数有 : 。 既是 2 的倍数又是 5 的倍数有 : 。 12 , 18 , 20 , 48 , 60 , 72 , , 25 , 60 ,
Advertisements

7.1 内置对象概述及分类 JSP 视频教学课程. JSP2.2 目录 1. 内置对象简介 1. 内置对象简介 2. 内置对象分类 2. 内置对象分类 3. 内置对象按功能区分 3. 内置对象按功能区分 4. 内置对象作用范围 4. 内置对象作用范围.
阻塞操作. 在 linux 里,一个等待队列由一个 wait_queue_head_t 类型的结构来描述 等待队列的初始化: static wait_queue_head_t testqueue; init_waitqueue_head(&testqueue);
LSF系统介绍 张焕杰 中国科学技术大学网络信息中心
Oracle数据库 Oracle 子程序.
第五章 C/OS-II在ARM系统中的应用与开发
嵌入式系统概论 —基于32位微处理器与实时操作系统 第五讲实时操作系统C/OS-Ⅱ分析 北京航空航天大学 机器人研究所 魏洪兴.
在PHP和MYSQL中实现完美的中文显示
EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构;并用操作语义的方法描述对应的语义规则
Kvm异步缺页中断 浙江大学计算机体系结构实验室 徐浩.
LSF系统介绍 张焕杰 中国科学技术大学网络信息中心
VxWorks软硬件设计及实例分析
嵌入式系统及应用.
嵌入式操作系统ucOS-II分析.
第7章 移植μC/OS-II到ARM7.
嵌入式系统及应用.
《手把手教你学STM32-UCOS》 主讲人 :正点原子团队 硬件平台:正点原子STM32开发板 版权所有:广州市星翼电子科技有限公司
UCOS -II的使用 撰写:李湧 2006-06-29.
《手把手教你学STM32-UCOS》 主讲人 :正点原子团队 硬件平台:正点原子STM32开发板 版权所有:广州市星翼电子科技有限公司
辅导课程六.
UcOS-II内存管理.
网络常用常用命令 课件制作人:谢希仁.
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
Windows网络操作系统管理 ——Windows Server 2008 R2.
移植μC/OS-Ⅱ.
嵌入式系统 —嵌入式实时操作系统C/OS-Ⅱ分析 2006年5月.
第48组:姜立群(SC ) 谭兆路(SC ) 闫 佼(SC )
第五讲 C/OS-Ⅱ移植分析和系统初始化
本节内容 模拟线程切换 视频提供:昆山滴水信息技术有限公司 官网地址: 论坛地址: QQ交流 :
逆向工程-汇编语言
ΜC/OSⅡ中的任务调度 Group01小组 柴永锋 李逢春 苗 冬.
UcOS-II时间管理.
嵌入式系统设计与实例开发 ——ARM与C/OS-Ⅱ 北京航空航天大学 智能嵌入式技术工作室 王田苗 魏洪兴.
CPU结构和功能.
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
3. µC/OS-II内核 2019/4/11.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构;并用操作语义的方法描述对应的语义规则
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
顺序表的删除.
5. µC/OS-II应用实例.
UcOS-II任务之间的通讯与同步.
本节内容 随机读取 视频提供:昆山爱达人信息技术有限公司.
姚金宇 MIT SCHEME 使用说明 姚金宇
6. 面向任务程序设计(TOP).
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
Ch6. uC/OS-II分析 宋健建 南京大学软件学院.
信号量(Semaphore).
本节内容 结构体 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
数据报分片.
Chapter 18 使用GRASP的对象设计示例.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
Ch7 uC/OS-II分析(2) 宋健建 南京大学软件学院 2006/11.
临界区问题的硬件指令解决方案 (Synchronization Hardware)
本节内容 C语言的汇编表示 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
本节内容 Windows线程切换_时钟中断切换 视频提供:昆山滴水信息技术有限公司 官网地址: 论坛地址: QQ交流 :
基于列存储的RDF数据管理 朱敏
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
Chinese Virtual Observatory
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
《手把手教你学STM32-UCOS》 主讲人 :正点原子团队 硬件平台:正点原子STM32开发板 版权所有:广州市星翼电子科技有限公司
本节内容 进程 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
创建、启动和关闭Activity 本讲大纲: 1、创建Activity 2、配置Activity 3、启动和关闭Activity
使用Fragment 本讲大纲: 1、创建Fragment 2、在Activity中添加Fragment
RefWorks使用指南 归档、管理个人参考文献.
本节内容 SEMAPHORE 视频提供:昆山滴水信息技术有限公司 官网地址: 论坛地址: QQ交流 :
多个Activity的使用 本讲大纲: 1、使用Bundle在Activity之间交换数据 2、调用另一个Activity并返回结果
Presentation transcript:

ucOS-II任务管理

任务管理包括:在用户的应用程序中建立任务、删除任务、改变任务的优先级、挂起和恢复任务,以及获得有关任务的信息。 μC/OS-Ⅱ可以管理多达64个任务,并从中保留了四个最高优先级和四个最低优先级的任务供自己使用,用户可以使用的只有56个任务。 任务的优先级越高,反映优先级的值越低,任务的优先级数也可作为任务的标识符使用。

建立任务 用户可以通过以下两个函数之一来建立任务:OSTaskCreate() 或 OSTaskCreateExt()。 任务可以在多任务调度(即调用OSStart())开始前建立,也可以在其它任务的执行过程中被建立。 在开始多任务调度前,用户必须建立至少一个任务。 任务不能由中断服务程序(ISR)来建立。

OSTaskCreate()程序 INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio) /task是任务代码指针,pdata是传递给任务的参数的指针,ptos是分配给任务的堆栈栈顶指针,prio是分配给任务的优先级 { void *psp; INT8U err;   if (prio > OS_LOWEST_PRIO) {(1) return (OS_PRIO_INVALID);}/检测分配给任务的优先级是否有效,任务的优先级必须在0到OS_LOWEST_PRIO之间 OS_ENTER_CRITICAL(); if (OSTCBPrioTbl[prio] == (OS_TCB *)0) {(2)/确保在规定的优先级上还没有建立任务 OSTCBPrioTbl[prio] = (OS_TCB *)1; (3)/通过放置一个非空指针在OSTCBPrioTbl[]中来保留该优先级 OS_EXIT_CRITICAL(); (4) /设置任务数据结构的其他部分时重新允许中断 psp = (void *)OSTaskStkInit(task, pdata, ptos, 0); (5) /建立任务的堆栈,OSTaskStkInit()函数返回新的堆栈栈顶(psp),并保存在任务的0S_TCB中。 err = OSTCBInit(prio, psp, (void *)0, 0, 0, (void *)0, 0); (6) /调用OSTCBInit(),从空闲的OS_TCB池中获得并初始化一个OS_TCB。 if (err == OS_NO_ERR) {(7)/返回一个代码表明OS_TCB已经被分配和初始化 OSTaskCtr++; (8)/如果OSTCBInit()返回成功,就增加OSTaskCtr(用于保存产生的任务数目) OSTaskCreateHook(OSTCBPrioTbl[prio]); (9)/用户自己定义的函数,用来扩展OSTaskCreate()的功能。 OS_EXIT_CRITICAL(); if (OSRunning) {(10)/如果OSTaskCreate()函数是在某个任务的执行过程中被调用(即OSRunning置为True ) OSSched(); (11)}} /调用任务调度函数以判断是否新建立的任务比原来的任务有更高的优先级 else {OS_ENTER_CRITICAL(); OSTCBPrioTbl[prio] = (OS_TCB *)0; (12) /如果OSTCBInit()返回失败,就置OSTCBPrioTbl[prio]的入口为0,以放弃该任务的优先级 OS_EXIT_CRITICAL();} return (err); } else {OS_EXIT_CRITICAL(); return (OS_PRIO_EXIST); } }

OSTCBInit()程序 INT8U OSTCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT16U stk_size, void *pext, INT16U opt) { OS_TCB *ptcb;   OS_ENTER_CRITICAL(); ptcb = OSTCBFreeList; (1) /从OS_TCB缓冲池中获得一个OS_TCB if (ptcb != (OS_TCB *)0) {(2) /如果OS_TCB池中有空闲的OS_TCB,则继续下面的操作 OSTCBFreeList = ptcb->OSTCBNext; OS_EXIT_CRITICAL(); ptcb->OSTCBStkPtr = ptos; (3) /初始化OS_TCB ptcb->OSTCBPrio = (INT8U)prio; ptcb->OSTCBStat = OS_STAT_RDY; ptcb->OSTCBDly = 0; #if OS_TASK_CREATE_EXT_EN ptcb->OSTCBExtPtr = pext; ptcb->OSTCBStkSize = stk_size; ptcb->OSTCBStkBottom = pbos; ptcb->OSTCBOpt = opt; ptcb->OSTCBId = id; #else pext = pext; stk_size = stk_size; pbos = pbos; opt = opt; id = id; #endif  #if OS_TASK_DEL_EN ptcb->OSTCBDelReq = OS_NO_ERR; #endif

OSTCBInit()程序(续) ptcb->OSTCBY = prio >> 3; ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY]; ptcb->OSTCBX = prio & 0x07; ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];  #if OS_MBOX_EN || (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_SEM_EN ptcb->OSTCBEventPtr = (OS_EVENT *)0; #endif  #if OS_MBOX_EN || (OS_Q_EN && (OS_MAX_QS >= 2)) ptcb->OSTCBMsg = (void *)0; OS_ENTER_CRITICAL(); (4) /禁止中断 OSTCBPrioTbl[prio]= ptcb; (5) /将OS_TCB插入到已建立任务的OS_TCB的双向链表中 ptcb->OSTCBNext = OSTCBList; ptcb->OSTCBPrev = (OS_TCB *)0; if (OSTCBList != (OS_TCB *)0) {OSTCBList->OSTCBPrev = ptcb;} OSTCBList = ptcb; OSRdyGrp|= ptcb->OSTCBBitY; (6) /任务处于就绪状态 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; OS_EXIT_CRITICAL(); return (OS_NO_ERR); (7) /OSTCBInit()向它的调用者[OSTaskCreate()]返回一个代码表明OS_TCB已经被分配和初始化 } else {OS_EXIT_CRITICAL(); return (OS_NO_MORE_TCB); } }

OSTaskCreateExt() 用OSTaskCreateExt()函数来建立任务更加灵活,但会增加一些额外的开销。 OSTaskCreateExt()需要九个参数,前四个参数(task,pdata,ptos和prio)与OSTaskCreate()的四个参数完全相同。这样用户能够更容易地将程序从OSTaskCreate()移植到OSTaskCreateExt()上去。 id参数为要建立的任务创建一个特殊的标识符,在μC/OS-Ⅱ中还未使用。用户只要将任务的id设置成与任务的优先级一样的值就可以了。 pbos是指向任务的堆栈栈底的指针,用于堆栈的检验。 stk_size用于指定堆栈成员数目的容量,也用于堆栈的检验。 pext是指向用户附加的数据域的指针,用来扩展任务的OS_TCB。 opt用于设定OSTaskCreateExt()的选项,指定是否允许堆栈检验,是否将堆栈清零,任务是否要进行浮点操作等等。

OSTaskCreateExt()程序 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) /9个参数 { void *psp; INT8U err; INT16U i; OS_STK *pfill;   if (prio > OS_LOWEST_PRIO) {(1) /检测分配给任务的优先级是否有效,任务的优先级必须在0到OS_LOWEST_PRIO之间 return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); if (OSTCBPrioTbl[prio] == (OS_TCB *)0) {(2) /确保在规定的优先级上还没有建立任务 OSTCBPrioTbl[prio] = (OS_TCB *)1; (3) /放置一个非空指针在OSTCBPrioTbl[]中来保留该空闲的优先级 OS_EXIT_CRITICAL(); (4) /在设置任务数据结构的其他部分时重新允许中断 if (opt & OS_TASK_OPT_STK_CHK) {(5) /在opt参数中设置OS_TASK_OPT_STK_CHK标志 if (opt & OS_TASK_OPT_STK_CLR) { /在opt参数中设置OS_TASK_OPT_STK_CLR标志 Pfill = pbos; for (i = 0; i < stk_size; i++) { /堆栈清零 #if OS_STK_GROWTH == 1 *pfill++ = (OS_STK)0; #else *pfill-- = (OS_STK)0; #endif } }

OSTaskCreateExt()程序(续) psp = (void *)OSTaskStkInit(task, pdata, ptos, opt); (6) /建立任务的堆栈,返回新的堆栈栈顶(psp),并保存在任务的0S_TCB中。 err = OSTCBInit(prio, psp, pbos, id, stk_size, pext, opt); (7) /从空闲的OS_TCB缓冲池中获得并初始化一个OS_TCB if (err == OS_NO_ERR) {(8) /检验OSTCBInit()的返回代码 OS_ENTER_CRITICAL(); OSTaskCtr++; (9) /如果返回成功就增加OSTaskCtr,以保存产生的任务数目 OSTaskCreateHook(OSTCBPrioTbl[prio]); (10) /用户自己定义的函数,用来扩展OSTaskCreateExt()的功能。 OS_EXIT_CRITICAL(); if (OSRunning) {(11) /如果OSTaskCreateExt()函数是在某个任务的执行过程中被调用的(即OSRunning置为True) OSSched(); (12) /调用任务调度函数以判断新建立的任务是否比原来的任务优先级更高} } else {OS_ENTER_CRITICAL(); OSTCBPrioTbl[prio] = (OS_TCB *)0; (13) /如果OSTCBInit()返回失败,就置OSTCBPrioTbl[prio]的入口为0以放弃对该任务优先级的占用 OS_EXIT_CRITICAL(); } return (err); } else {OS_EXIT_CRITICAL(); return (OS_PRIO_EXIST); }

任务堆栈 每个任务都有自己的堆栈空间。 堆栈必须声明为OS_STK类型,并且由连续的内存空间组成。 用户可以静态分配堆栈空间(在编译的时候分配)也可以动态地分配堆栈空间(在运行的时候分配)。 静态堆栈与动态分配堆栈的声明应放置在函数的外面。

静态堆栈与动态分配堆栈 static OS_STK MyTaskStack[stack_size]; OS_STK *pstk;   pstk = (OS_STK *)malloc(stack_size); if (pstk != (OS_STK *)0) { /* 确认malloc()能得到足够的内存空间 */ Create the task;} 动态分配堆栈时,要时刻注意内存碎片问题。特别是当用户反复建立和删除任务时,内存堆中可能会出现大量的内存碎片,导致没有足够大的一块连续内存区域可用作任务堆栈,这时malloc()便无法成功地为任务分配堆栈空间。如果用户不去删除任务,那么使用malloc()则是非常可行的。

内存碎片 假定用户要建立三个任务(任务A、B和C),每个任务需要1K字节的空间。 设第一个1K字节给任务A,第二个1K字节给任务B,第三个1K字节给任务C。 然后,用户的应用程序删除任务A和任务C,用free()函数释放内存到内存堆中。 现在,用户的内存堆虽有2K字节的自由内存空间,但它是不连续的,所以用户不能建立另一个需要2K字节内存的任务(如任务D)。

堆栈生长方向 μC/OS-Ⅱ支持的处理器的堆栈既可以从上(高地址)往下(低地址)长也可以从下往上长。 用户在调用OSTaskCreate()或OSTaskCreateExt()的时候必须知道堆栈是怎样长的,以便把堆栈的栈顶传递给以上两个函数。

堆栈从下往上递增程序 当OS_CPU.H文件中的OS_STK_GROWTH置为0时,用户需要将堆栈的最低内存地址传递给任务创建函数。 OS_STK TaskStack[TASK_STACK_SIZE];  OSTaskCreate(task, pdata, &TaskStack[0], prio);

堆栈从上往下递减程序 当OS_CPU.H文件中的OS_STK_GROWTH置为1时,用户需要将堆栈的最高内存地址传递给任务创建函数。 OS_STK TaskStack[TASK_STACK_SIZE];  OSTaskCreate(task, pdata, &TaskStack[TASK_STACK_SIZE-1], prio);

对两个方向增长的堆栈都提供支持的程序 OS_STK TaskStack[TASK_STACK_SIZE];    #if OS_STK_GROWTH == 0 OSTaskCreate(task, pdata, &TaskStack[0], prio); #else OSTaskCreate(task, pdata, &TaskStack[TASK_STACK_SIZE-1], prio); #endif 任务所需的堆栈的容量是由应用程序指定的。用户在指定堆栈大小的时候必须考虑用户的任务所调用的所有函数的嵌套情况,以及所有可能的中断服务程序嵌套的堆栈需求。另外,用户的堆栈必须能储存所有的CPU寄存器。

堆栈检验 有时候决定任务实际所需的堆栈空间大小是很有必要的。这样就可以避免为任务分配过多的堆栈空间,从而减少用户应用程序代码所需的RAM(内存)数量。 μC/OS-Ⅱ提供的OSTaskStkChk()函数可以为用户提供这种有价值的信息。

堆栈检验(续) 为了使用μC/OS-Ⅱ的堆栈检验功能,必须做以下几件事情: 在OS_CFG.H文件中设OS_TASK_CREATE_EXT为1。 用OSTaskCreateExt()建立任务,并给予任务比实际需要更多的内存空间。 在OSTaskCreateExt()中,将参数opt设置为OS_TASK_OPT_STK_CHK+OS_TASK_OPT_STK_CLR。注意如果用户的程序启动代码清除了所有的RAM,并且从未删除过已建立的任务,那么就不必设置选项OS_TASK_OPT_STK_CLR,这样就会减少OSTaskCreateExt()的执行时间。 将用户想检验的任务的优先级作为OSTaskStkChk()的参数并调用。

堆栈检验(续) 用户应该使自己的应用程序运行足够长的时间,并且经历最坏的堆栈使用情况,这样才能得到正确的堆栈数值。一旦OSTaskStkChk()提供给用户最坏情况下堆栈的需求,用户就可以重新设置堆栈的容量了。 为了适应系统以后的升级和扩展,用户应该多分配10%-100%的堆栈空间。 在堆栈检验中,用户所得到的只是一个大致的堆栈使用情况,并不能说明堆栈使用的全部实际情况。

堆栈检验函数程序 INT8U OSTaskStkChk (INT8U prio, OS_STK_DATA *pdata) { /0S_STK_DATA(参看μCOS_Ⅱ.H)数据结构用来保存有关任务堆栈的信息 OS_TCB *ptcb; OS_STK *pchk; INT32U free; INT32U size;  pdata->OSFree = 0; pdata->OSUsed = 0; /0S_STK_DATA只包含两个域:OSFree和OSUsed if (prio > OS_LOWEST_PRIO && prio != OS_PRIO_SELF) {return (OS_PRIO_INVALID);} OS_ENTER_CRITICAL(); if (prio == OS_PRIO_SELF) {(1) /指定0S_PRIO_SELF,表明用户想知道当前任务的堆栈信息。 prio = OSTCBCur->OSTCBPrio;} ptcb = OSTCBPrioTbl[prio]; if (ptcb == (OS_TCB *)0) {(2) /如果任务不存在 OS_EXIT_CRITICAL(); return (OS_TASK_NOT_EXIST); }

堆栈检验函数程序(续) if ((ptcb->OSTCBOpt & OS_TASK_OPT_STK_CHK) == 0) { (3) /要执行堆栈检验,用户必须用OSTaskCreateExt()建立了任务并且传递了选项OS_TASK_OPT_CHK,用OSTaskCreate()建立任务会出错。 OS_EXIT_CRITICAL(); return (OS_TASK_OPT_ERR); } free = 0; (4) /如果所有条件都满足,OSTaskStkChk()就会从堆栈栈底开始统计堆栈的空闲空间 size = ptcb->OSTCBStkSize; pchk = ptcb->OSTCBStkBottom; #if OS_STK_GROWTH == 1 while (*pchk++ == 0) {free++;} #else while (*pchk-- == 0) {free++;} #endif pdata->OSFree = free * sizeof(OS_STK); (5) /储存在0S_STK_DATA中的信息被确定下来,包括堆栈的实际空闲字节数和已被占用的字节数(二者之和为堆栈的实际大小)。 pdata->OSUsed = (size - free) * sizeof(OS_STK); return (OS_NO_ERR); }

删除任务 有时候删除任务是很有必要的。 删除任务,即任务将返回并处于休眠状态,任务的代码不会被删除,只是任务的代码不再被μC/OS-Ⅱ调用。

删除任务程序 INT8U OSTaskDel (INT8U prio) { OS_TCB *ptcb; OS_EVENT *pevent; if (prio == OS_IDLE_PRIO) { (1) /不允许删除空闲任务 return (OS_TASK_DEL_IDLE);} if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { (2) /可以删除statistic任务 return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); if (OSIntNesting > 0) { (3) /不允许在ISR例程中去删除一个任务 OS_EXIT_CRITICAL(); return (OS_TASK_DEL_ISR);} if (prio == OS_PRIO_SELF) { (4) /调用此函数的任务可以通过指定OS_PRIO_SELF参数来删除自己 Prio = OSTCBCur->OSTCBPrio;}

删除任务程序(续) if ((ptcb = OSTCBPrioTbl[prio]) != (OS_TCB *)0) { (5) /保证被删除的任务是确实存在的 if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) { (6) /如果任务处于就绪表中,它会直接被移除 OSRdyGrp &= ~ptcb->OSTCBBitY; } if ((pevent = ptcb->OSTCBEventPtr) != (OS_EVENT *)0) { (7) /如果任务处于邮箱、消息队列或信号量的等待表中,它就从自己所处的表中被移除 if ((pevent->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {pevent->OSEventGrp &= ~ptcb->OSTCBBitY;} } Ptcb->OSTCBDly = 0; (8) /将任务的时钟延迟数清零,以确保自己重新允许中断的时候,ISR例程不会使该任务就绪 Ptcb->OSTCBStat = OS_STAT_RDY; (9) /置任务的.OSTCBStat标志为OS_STAT_RDY,以阻止其它任务或ISR例程让该任务重新开始执行(即避免其它任务或ISR调用OSTaskResume())。 OSLockNesting++; (10) /阻止任务调度程序在删除过程中切换到其它的任务中去 OS_EXIT_CRITICAL(); (11) /重新允许中断以减少中断的响应时间 OSDummy(); (12) /在中断允许的情况下至少执行一个指令,确保在再次禁止中断之前至少执行了一个调用指令和一个返回指令。

删除任务程序(续) OS_ENTER_CRITICAL(); OSLockNesting--; (13) /重新关中断后,通过锁定嵌套计数器(OSLockNesting)减一以重新允许任务调度 OSTaskDelHook(ptcb); (14) /用户自定义的函数,可以在这里删除或释放自定义的TCB附加数据域 OSTaskCtr--; /减少μCOS-Ⅱ的任务计数器 OSTCBPrioTbl[prio] = (OS_TCB *)0; (15) /将指向被删除的任务的OS_TCB的指针指向NULL,从而达到将OS_TCB从优先级表中移除的目的。 If (ptcb->OSTCBPrev == (OS_TCB *)0) { (16) /将被删除的任务的OS_TCB从OS_TCB双向链表中移除。注意,没有必要检验ptcb->OSTCBNext==0的情况,因为OSTaskDel()不能删除空闲任务,而空闲任务就处于链表的末端(ptcb->OSTCBNext==0)。 ptcb->OSTCBNext->OSTCBPrev = (OS_TCB *)0; OSTCBList = ptcb->OSTCBNext; } else {ptcb->OSTCBPrev->OSTCBNext = ptcb->OSTCBNext; ptcb->OSTCBNext->OSTCBPrev = ptcb->OSTCBPrev;} ptcb->OSTCBNext = OSTCBFreeList; (17) /OS_TCB返回到空闲OS_TCB表中,并允许其它任务建立时使用。 OSTCBFreeList = ptcb; OS_EXIT_CRITICAL(); OSSched(); (18) /调用任务调度程序来查看在OSTaskDel()重新允许中断的时候(11),中断服务子程序是否曾使更高优先级的任务处于就绪状态 return (OS_NO_ERR); } else {OS_EXIT_CRITICAL(); return (OS_TASK_DEL_ERR); } }

请求删除任务 有时候,如果任务A拥有内存缓冲区或信号量之类的资源,而任务B想删除该任务,这些资源就可能由于没被释放而丢失。在这种情况下,用户可以想法让拥有这些资源的任务在使用完资源后,先释放资源,再删除自己,可以通过OSTaskDelReq()函数来完成该功能。 发出删除任务请求的任务(任务B)和要删除的任务(任务A)都需要调用OSTaskDelReq()函数。

请求删除其它任务的任务(任务B)程序 void RequestorTask (void *pdata) { INT8U err;   pdata = pdata; for (;;) { /* 应用程序代码 */ if ('TaskToBeDeleted()' 需要被删除) {(1) /决定在怎样的情况(条件)下请求删除任务 while (OSTaskDelReq(TASK_TO_DEL_PRIO) != OS_TASK_NOT_EXIST) { (2) /如果任务需要被删除,可以通过传递被删除任务的优先级来调用OSTaskDelReq() OSTimeDly(1); (3) /任务B延时一个时钟节拍(或更长时间),直到任务A删除了自己以后才继续进行下面的工作 } } /*应用程序代码*/ (4) /当任务A完全删除自己后,(2)中的返回值成为0S_TASK_NOT_EXIST,此时循环结束 }

需要删除自己的任务(任务A)程序 void TaskToBeDeleted (void *pdata) { INT8U err;   pdata = pdata; for (;;) { /*应用程序代码*/ /在OS_TCB中有一个标志,任务通过查询这个标志的值来确认自己是否需要被删除。这个标志的值是通过调用OSTaskDelReq(OS_PRIO_SELF)而得到。 If (OSTaskDelReq(OS_PRIO_SELF) == OS_TASK_DEL_REQ) { (1) /当OSTaskDelReq()返回OS_TASK_DEL_REQ给调用者时,表明已经有另外的任务请求删除该任务了。 释放所有占用的资源; (2) /被删除的任务释放它所拥有的资源 释放所有动态内存; OSTaskDel(OS_PRIO_SELF); (3) /调用OSTaskDel(OS_PRIO_SELF)来删除自己 } else {/*应用程序代码*/} }

OSTaskDelReq()程序 INT8U OSTaskDelReq (INT8U prio) { BOOLEAN stat; INT8U err; OS_TCB *ptcb;   if (prio == OS_IDLE_PRIO) {(1) /检查临界条件,如果正在删除的任务是空闲任务,OSTaskDelReq()会报错并返回 return (OS_TASK_DEL_IDLE); } if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) {(2) /保证调用者请求删除的任务的优先级是有效的 return (OS_PRIO_INVALID); } if (prio == OS_PRIO_SELF) {(3) /如果调用者就是被删除任务本身,存储在OS_TCB中的标志将会作为返回值 OS_ENTER_CRITICAL(); stat = OSTCBCur->OSTCBDelReq; OS_EXIT_CRITICAL(); return (stat); } else { OS_ENTER_CRITICAL(); if ((ptcb = OSTCBPrioTbl[prio]) != (OS_TCB *)0) {(4) /如果用户用优先级而不是OS_PRIO_SELF指定任务,并且任务是存在的 ptcb->OSTCBDelReq = OS_TASK_DEL_REQ; (5) /设置任务的内部标志 err = OS_NO_ERR; } else {err = OS_TASK_NOT_EXIST; (6) /如果任务不存在,OSTaskDelReq()则会返回OS_TASK_NOT_EXIST,表明任务可能已经删除自己了 } OS_EXIT_CRITICAL(); return (err); } }

改变任务的优先级 在用户建立任务的时候会分配给任务一个优先级。 在程序运行期间,用户可以通过调用OSTaskChangePrio()来改变任务的优先级。 μC/OS-Ⅱ允许用户动态改变任务的优先级。

OSTaskChangePrio()程序 INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio) { OS_TCB *ptcb; OS_EVENT *pevent; INT8U x; INT8U y; INT8U bitx; INT8U bity;   if ((oldprio >= OS_LOWEST_PRIO && oldprio != OS_PRIO_SELF) || newprio >= OS_LOWEST_PRIO) (1) /不能改变空闲任务的优先级 {return (OS_PRIO_INVALID);} OS_ENTER_CRITICAL(); if (OSTCBPrioTbl[newprio] != (OS_TCB *)0) {(2) /检验新优先级是否空闲(即不存在具有相同优先级的任务) OS_EXIT_CRITICAL(); return (OS_PRIO_EXIST); } else {OSTCBPrioTbl[newprio] = (OS_TCB *)1; (3) /如果新优先级空闲,则保留这个优先级 OS_EXIT_CRITICAL(); y = newprio >> 3; (4) /预先计算新优先级任务的OS_TCB中的某些值(用于将任务放入或移出就绪表) bity = OSMapTbl[y]; x = newprio & 0x07; bitx = OSMapTbl[x];

OSTaskChangePrio()程序(续) if (oldprio == OS_PRIO_SELF) {(5) /检验当前任务是否想改变它的优先级 oldprio = OSTCBCur->OSTCBPrio; } if ((ptcb = OSTCBPrioTbl[oldprio]) != (OS_TCB *)0) {(6) /检查想要改变优先级的任务是否存在 OSTCBPrioTbl[oldprio] = (OS_TCB *)0; (7) /如果要改变优先级的任务就是当前任务,则通过插入NULL指针将指向当前任务OS_TCB的指针从优先级表中移除 if ((OSRdyTbl[ptcb->OSTCBY] & ptcb->OSTCBBitX)!=0) {(8) /检验一下OSTaskChangePrio()想要改变优先级的任务是否就绪 if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {(9) /如果该任务处于就绪状态,它必须在当前的优先级下从就绪表中移除 OSRdyGrp &= ~ptcb->OSTCBBitY; } OSRdyGrp |= bity; (10) /在新的优先级下,用预先计算的值(4)将任务插入到就绪表中 OSRdyTbl[y] |= bitx; } else {if ((pevent = ptcb->OSTCBEventPtr) != (OS_EVENT *)0) {(11) /如果任务未就绪(OSTCBEventPtr非空 (8)) ,它可能正在等待一个信号量、一封邮件或是一个消息队列。 if ((pevent->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {pevent->OSEventGrp &= ~ptcb->OSTCBBitY;} pevent->OSEventGrp |= bity; (12) /如果任务在等待某一事件的发生,OSTaskChangePrio()必须将任务从事件控制块的等待队列(在旧的优先级下)中移除,并在新的优先级下将事件插入到等待队列中。 pevent->OSEventTbl[y] |= bitx; } }

OSTaskChangePrio()程序(续) OSTCBPrioTbl[newprio] = ptcb; (13) /将指向任务OS_TCB的指针存到OSTCBPrioTbl[]中 ptcb->OSTCBPrio = newprio; (14) /将新的优先级保存到OS_TCB中 ptcb->OSTCBY = y; (15) /将预先计算的值也保存在OS_TCB中 ptcb->OSTCBX = x; ptcb->OSTCBBitY = bity; ptcb->OSTCBBitX = bitx; OS_EXIT_CRITICAL(); OSSched(); (16) /在新的优先级高于旧的优先级或新的优先级高于调用本函数的任务的优先级情况下,任务调度程序就会被调用 return (OS_NO_ERR); } else {OSTCBPrioTbl[newprio] = (OS_TCB *)0; (17) /如果OSTaskChangePrio()想要改变优先级的任务不存在,它必须将保留的新优先级放回到优先级表OSTCBPrioTbl[]中,并返回一个错误码给调用者。 OS_EXIT_CRITICAL(); return (OS_PRIO_ERR);} }

挂起任务 有时候将任务挂起是很有用的。 挂起任务可通过调用OSTaskSuspend()函数来完成。 被挂起的任务只能通过调用OSTaskResume()函数来恢复。 任务挂起是一个附加功能,如果任务在被挂起的同时也在等待延时的期满,那么需要对任务做取消挂起操作,并继续等待延时期满,才能转入就绪状态。 任务可以挂起自己或者其它任务。

OSTaskSuspend()程序 INT8U OSTaskSuspend (INT8U prio) { BOOLEAN self; OS_TCB *ptcb;   if (prio == OS_IDLE_PRIO) {(1) /确保用户的应用程序不是在挂起空闲任务(可以挂起统计任务) return (OS_TASK_SUSPEND_IDLE); } if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) {(2) /确认用户指定优先级是有效的 return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); if (prio == OS_PRIO_SELF) {(3) /检验用户是否通过指定OS_PRIO_SELF来挂起调用本函数的任务本身 prio = OSTCBCur->OSTCBPrio; self = TRUE; } else if (prio == OSTCBCur->OSTCBPrio) {(4) /也可以通过指定优先级来挂起调用本函数的任务 self = TRUE; } else {self = FALSE; } if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) {(5) /检验要挂起的任务是否存在 OS_EXIT_CRITICAL(); return (OS_TASK_SUSPEND_PRIO);} else {if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {(6) /如果该任务存在的话,它就会从就绪表中被移除 OSRdyGrp &= ~ptcb->OSTCBBitY;} ptcb->OSTCBStat |= OS_STAT_SUSPEND; (7) /在任务的OS_TCB中设置OS_STAT_SUSPEND标志了,以表明任务正在被挂起 OS_EXIT_CRITICAL(); if (self == TRUE) {(8) /只有在被挂起的任务是调用本函数的任务本身的情况下,才调用任务调度程序 OSSched();} return (OS_NO_ERR); } }

恢复任务 被挂起的任务只有通过调用OSTaskResume()才能恢复。 要恢复的任务必须是存在的。

OSTaskResume()程序 INT8U OSTaskResume (INT8U prio) { OS_TCB *ptcb; If (prio >= OS_LOWEST_PRIO) {(1) /确认用户的应用程序不是在恢复空闲任务 return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); If ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) {(2) /设置要恢复任务的任务控制块OS_TCB OS_EXIT_CRITICAL(); return (OS_TASK_RESUME_PRIO); } else {if (ptcb->OSTCBStat & OS_STAT_SUSPEND) {(3) /该任务必须是被挂起的 if (((ptcb->OSTCBStat &= ~OS_STAT_SUSPEND) == OS_STAT_RDY) && (4) /通过清除OSTCBStat域中的OS_STAT_SUSPEND位来取消挂起/ (ptcb->OSTCBDly == 0)) {(5) /要使任务处于就绪状态,OS_TCBDly域必须为0 OSRdyGrp|= ptcb->OSTCBBitY; (6) /当以上两个条件都满足时,任务才处于就绪状态 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; OS_EXIT_CRITICAL(); OSSched(); (7) /检查被恢复的任务拥有的优先级是否比调用本函数的任务的优先级高 } else {OS_EXIT_CRITICAL();} return (OS_NO_ERR); } else {OS_EXIT_CRITICAL(); return (OS_TASK_NOT_SUSPENDED);} } }

获得有关任务的信息 用户的应用程序可以通过调用OSTaskQuery()来获得自身或其它应用任务的信息。 实际上,OSTaskQuery()获得的是对应任务的OS_TCB中内容的拷贝。 用户能访问的OS_TCB的数据域的多少决定于用户的应用程序的配置(参看OS_CFG.H) ,它只包括那些用户的应用程序所要求的属性和功能。 一般来说,本函数只用来了解任务正在干什么——本函数是有用的调试工具。

得到任务信息的程序 OS_TCB MyTaskData; void MyTask (void *pdata) { pdata = pdata; for (;;) { /* 用户代码 */ err = OSTaskQuery(10, &MyTaskData); /* Examine error code .. */ /* 用户代码*/ } }

OSTaskQuery()程序 INT8U OSTaskQuery (INT8U prio, OS_TCB *pdata) { OS_TCB *ptcb;  if (prio > OS_LOWEST_PRIO && prio != OS_PRIO_SELF) {(1) /查询所有的任务,包括空闲任务 return (OS_PRIO_INVALID);} OS_ENTER_CRITICAL(); if (prio == OS_PRIO_SELF) {(2) /检验用户是否想知道当前任务的有关信息 prio = OSTCBCur->OSTCBPrio; } if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) {(3) /检验该任务是否已经建立 OS_EXIT_CRITICAL(); return (OS_PRIO_ERR);} *pdata = *ptcb; (4) /通过赋值语句一次性复制所有的域 OS_EXIT_CRITICAL(); return (OS_NO_ERR); }