程序清单 L 4.11 删除任务 |
INT8U OSTaskDel (INT8U prio)< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" /> |
{ |
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) |
return (OS_PRIO_INVALID); |
} |
OS_ENTER_CRITICAL(); |
if (OSIntNesting > 0) { (3) |
OS_EXIT_CRITICAL(); |
return (OS_TASK_DEL_ISR); |
} |
if (prio == OS_PRIO_SELF) { (4) |
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) |
Ptcb->OSTCBStat = OS_STAT_RDY; (9) |
OSLockNesting++; (10) |
OS_EXIT_CRITICAL(); (11) |
OSDummy(); (12) |
OS_ENTER_CRITICAL(); |
OSLockNesting--; (13) |
OSTaskDelHook(ptcb); (14) |
OSTaskCtr--; |
OSTCBPrioTbl[prio] = (OS_TCB *)0; (15) |
if (ptcb->OSTCBPrev == (OS_TCB *)0) { (16) |
ptcb->OSTCBNext->OSTCBPrev = (OS_TCB *)0; |
OSTCBList = ptcb->OSTCBNext; |
} else { |
ptcb->OSTCBPrev->OSTCBNext = ptcb->OSTCBNext; |
ptcb->OSTCBNext->OSTCBPrev = ptcb->OSTCBPrev; |
} |
ptcb->OSTCBNext = OSTCBFreeList; (17) |
OSTCBFreeList = ptcb; |
OS_EXIT_CRITICAL(); |
OSSched(); (18) |
return (OS_NO_ERR); |
} else { |
OS_EXIT_CRITICAL(); |
return (OS_TASK_DEL_ERR); |
} |
} |
一旦所有条件都满足了,OS_TCB就会从所有可能的µC/OS-Ⅱ的数据结构中移除。OSTaskDel()分两步完成该移除任务以减少中断响应时间。首先,如果任务处于就绪表中,它会直接被移除[L4.11(6)]。如果任务处于邮箱、消息队列或信号量的等待表中,它就从自己所处的表中被移除[L4.11(7)]。接着,OSTaskDel()将任务的时钟延迟数清零,以确保自己重新允许中断的时候,ISR例程不会使该任务就绪[L4.11(8)]。最后,OSTaskDel()置任务的.OSTCBStat标志为OS_STAT_RDY。注意,OSTaskDel()并不是试图使任务处于就绪状态,而是阻止其它任务或ISR例程让该任务重新开始执行(即避免其它任务或ISR调用OSTaskResume()[L4.11(9)])。这种情况是有可能发生的,因为OSTaskDel()会重新打开中断,而ISR可以让更高优先级的任务处于就绪状态,这就可能会使用户想删除的任务重新开始执行。如果不想置任务的.OSTCBStat标志为OS_STAT_RDY,就只能清除OS_STAT_SUSPEND位了(这样代码可能显得更清楚,更容易理解一些),但这样会使得处理时间稍长一些。
要被删除的任务不会被其它的任务或ISR置于就绪状态,因为该任务已从就绪任务表中删除了,它不是在等待事件的发生,也不是在等待延时期满,不能重新被执行。为了达到删除任务的目的,任务被置于休眠状态。正因为这样,OSTaskDel()必须得阻止任务调度程序[L4.11(10)]在删除过程中切换到其它的任务中去,因为如果当前的任务正在被删除,它不可能被再次调度!接下来,OSTaskDel()重新允许中断以减少中断的响应时间[L4.11(11)]。这样,OSTaskDel()就能处理中断服务了,但由于它增加了OSLockNesting,ISR执行完后会返回到被中断任务,从而继续任务的删除工作。注意OSTaskDel()此时还没有完全完成删除任务的工作,因为它还需要从TCB链中解开OS_TCB,并将OS_TCB返回到空闲OS_TCB表中。
另外需要注意的是,笔者在调用OS_EXIT_CRITICAL()函数后,马上调用了OSDummy() [L4.11(12)],该函数并不会进行任何实质性的工作。这样做只是因为想确保处理器在中断允许的情况下至少执行一个指令。对于许多处理器来说,执行中断允许指令会强制CPU禁止中断直到下个指令结束!Intel 80x86和Zilog Z-80处理器就是如此工作的。开中断后马上关中断就等于从来没开过中断,当然这会增加中断的响应时间。因此调用OSDummy()确保在再次禁止中断之前至少执行了一个调用指令和一个返回指令。当然,用户可以用宏定义将OSDummy()定义为一个空操作指令(译者注:例如MC68HC08指令中的NOP指令),这样调用OSDummy()就等于执行了一个空操作指令,会使OSTaskDel()的执行时间稍微缩短一点。但笔者认为这种宏定义是没价值的,因为它会增加移植µCOS-Ⅱ的工作量。
现在,OSTaskDel()可以继续执行删除任务的操作了。在OSTaskDel()重新关中断后,它通过锁定嵌套计数器(OSLockNesting)减一以重新允许任务调度[L4.11(13)]。接着,OSTaskDel()调用用户自定义的OSTaskDelHook()函数[L4.11(14)],用户可以在这里删除或释放自定义的TCB附加数据域。然后,OSTaskDel()减少µCOS-Ⅱ的任务计数器。OSTaskDel()简单地将指向被删除的任务的OS_TCB的指针指向NULL[L4.11(15)],从而达到将OS_TCB从优先级表中移除的目的。再接着,OSTaskDel()将被删除的任务的OS_TCB从OS_TCB双向链表中移除[L4.11(16)]。注意,没有必要检验ptcb->OSTCBNext==0的情况,因为OSTaskDel()不能删除空闲任务,而空闲任务就处于链表的末端(ptcb->OSTCBNext==0)。接下来,OS_TCB返回到空闲OS_TCB表中,并允许其它任务的建立[L4.11(17)]。最后,调用任务调度程序来查看在OSTaskDel()重新允许中断的时候[L4.11(11)],中断服务子程序是否曾使更高优先级的任务处于就绪状态[L4.11(18)]。
4.0 请求删除任务,OSTaskDelReq()
有时候,如果任务A拥有内存缓冲区或信号量之类的资源,而任务B想删除该任务,这些资源就可能由于没被释放而丢失。在这种情况下,用户可以想法子让拥有这些资源的任务在使用完资源后,先释放资源,再删除自己。用户可以通过OSTaskDelReq()函数来完成该功能。
发出删除任务请求的任务(任务B)和要删除的任务(任务A)都需要调用OSTaskDelReq()函数。任务B的代码如程序清单 L4.12所示。任务B需要决定在怎样的情况下请求删除任务[L4.12(1)]。换句话说,用户的应用程序需要决定在什么样的情况下删除任务。如果任务需要被删除,可以通过传递被删除任务的优先级来调用OSTaskDelReq()[L4.12(2)]。如果要被删除的任务不存在(即任务已被删除或是还没被建立),OSTaskDelReq()返回OS_TASK_NOT_EXIST。如果OSTaskDelReq()的返回值为OS_NO_ERR,则表明请求已被接受但任务还没被删除。用户可能希望任务B等到任务A删除了自己以后才继续进行下面的工作,这时用户可以象笔者一样,通过让任务B延时一定时间来达到这个目的[L4.12(3)]。笔者延时了一个时钟节拍。如果需要,用户可以延时得更长一些。当任务A完全删除自己后,[L4.12(2)]中的返回值成为0S_TASK_NOT_EXIST,此时循环结束[L4.12(4)]。
程序清单 L 4.12 请求删除其它任务的任务(任务B) |
void RequestorTask (void *pdata) |
{ |
INT8U err; |
|
|
pdata = pdata; |
for (;;) { |
/* 应用程序代码 */ |
if ('TaskToBeDeleted()' 需要被删除) { (1) |
while (OSTaskDelReq(TASK_TO_DEL_PRIO) != OS_TASK_NOT_EXIST) { (2) |
OSTimeDly(1); (3) |
} |
} |
/*应用程序代码*/ (4) |
} |
} |
程序清单 L 4.13 需要删除自己的任务(任务A) |
void TaskToBeDeleted (void *pdata) |
{ |
INT8U err; |
|
|
pdata = pdata; |
for (;;) { |
/*应用程序代码*/ |
If (OSTaskDelReq(OS_PRIO_SELF) == OS_TASK_DEL_REQ) { (1) |
释放所有占用的资源; (2) |
释放所有动态内存; |
OSTaskDel(OS_PRIO_SELF); (3) |
} else { |
/*应用程序代码*/ |
} |
} |
} |
需要删除自己的任务(任务A)的代码如程序清单 L4.13所示。在OS_TAB中存有一个标志,任务通过查询这个标志的值来确认自己是否需要被删除。这个标志的值是通过调用OSTaskDelReq(OS_PRIO_SELF)而得到的。当OSTaskDelReq()返回给调用者OS_TASK_DEL_REQ[L4.13(1)]时,则表明已经有另外的任务请求该任务被删除了。在这种情况下,被删除的任务会释放它所拥有的所用资源[L4.13(2)],并且调用OSTaskDel(OS_PRIO_SELF)来删除自己[L4.13(3)]。前面曾提到过,任务的代码没有被真正的删除,而只是µC/OS-Ⅱ不再理会该任务代码,换句话说,就是任务的代码不会再运行了。但是,用户可以通过调用OSTaskCreate()或OSTaskCreateExt()函数重新建立该任务。
OSTaskDelReq()的代码如程序清单 L4.14所示。通常OSTaskDelReq()需要检查临界条件。首先,如果正在删除的任务是空闲任务,OSTaskDelReq()会报错并返回[L4.14(1)]。接着,它要保证调用者请求删除的任务的优先级是有效的[L4.14(2)]。如果调用者就是被删除任务本身,存储在OS_TCB中的标志将会作为返回值[L4.14(3)]。如果用户用优先级而不是OS_PRIO_SELF指定任务,并且任务是存在的[L4.14(4)],OSTaskDelReq()就会设置任务的内部标志[L4.14(5)]。如果任务不存在,OSTaskDelReq()则会返回OS_TASK_NOT_EXIST,表明任务可能已经删除自己了[L4.14(6)]。
程序清单 L 4.14 OSTaskDelReq(). |
INT8U OSTaskDelReq (INT8U prio) |
{ |
BOOLEAN stat; |
INT8U err; |
OS_TCB *ptcb; |
|
|
if (prio == OS_IDLE_PRIO) { (1) |
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_ENTER_CRITICAL(); |
stat = OSTCBCur->OSTCBDelReq; |
OS_EXIT_CRITICAL(); |
return (stat); |
} else { |
OS_ENTER_CRITICAL(); |
if ((ptcb = OSTCBPrioTbl[prio]) != (OS_TCB *)0) { (4) |
ptcb->OSTCBDelReq = OS_TASK_DEL_REQ; (5) |
err = OS_NO_ERR; |
} else { |
err = OS_TASK_NOT_EXIST; (6) |
} |
OS_EXIT_CRITICAL(); |
return (err); |
} |
} |
4.1 改变任务的优先级,OSTaskChangePrio()
在用户建立任务的时候会分配给任务一个优先级。在程序运行期间,用户可以通过调用OSTaskChangePrio()来改变任务的优先级。换句话说,就是µC/OS-Ⅱ允许用户动态的改变任务的优先级。
OSTaskChangePrio()的代码如程序清单 L4.15所示。用户不能改变空闲任务的优先级[L4.15(1)],但用户可以改变调用本函数的任务或者其它任务的优先级。为了改变调用本函数的任务的优先级,用户可以指定该任务当前的优先级或OS_PRIO_SELF,OSTaskChangePrio()会决定该任务的优先级。用户还必须指定任务的新(即想要的)优先级。因为µC/OS-Ⅱ不允许多个任务具有相同的优先级,所以OSTaskChangePrio()需要检验新优先级是否是合法的(即不存在具有新优先级的任务)[L4.15(2)]。如果新优先级是合法的,µC/OS-Ⅱ通过将某些东西储存到OSTCBPrioTbl[newprio]中保留这个优先级[L4.15(3)]。如此就使得OSTaskChangePrio()可以重新允许中断,因为此时其它任务已经不可能建立拥有该优先级的任务,也不能通过指定相同的新优先级来调用OSTaskChangePrio()。接下来OSTaskChangePrio()可以预先计算新优先级任务的OS_TCB中的某些值[L4.15(4)]。而这些值用来将任务放入就绪表或从该表中移除(参看3.04,就绪表)。
接着,OSTaskChangePrio()检验目前的任务是否想改变它的优先级[L4.15(5)]。然后,OSTaskChangePrio()检查想要改变优先级的任务是否存在[L4.15(6)]。很明显,如果要改变优先级的任务就是当前任务,这个测试就会成功。但是,如果OSTaskChangePrio()想要改变优先级的任务不存在,它必须将保留的新优先级放回到优先级表OSTCBPrioTbl[]中[L4.15(17)],并返回给调用者一个错误码。
现在,OSTaskChangePrio()可以通过插入NULL指针将指向当前任务OS_TCB的指针从优先级表中移除了[L4.15(7)]。这就使得当前任务的旧的优先级可以重新使用了。接着,我们检验一下OSTaskChangePrio()想要改变优先级的任务是否就绪[L4.15(8)]。如果该任务处于就绪状态,它必须在当前的优先级下从就绪表中移除[L4.15(9)],然后在新的优先级下插入到就绪表中[L4.15(10)]。这儿需要注意的是,OSTaskChangePrio()所用的是重新计算的值[L4.15(4)]将任务插入就绪表中的。
如果任务已经就绪,它可能会正在等待一个信号量、一封邮件或是一个消息队列。如果OSTCBEventPtr非空(不等于NULL)[L4.15(8)],OSTaskChangePrio()就会知道任务正在等待以上的某件事。如果任务在等待某一事件的发生,OSTaskChangePrio()必须将任务从事件控制块(参看6.00,事件控制块)的等待队列(在旧的优先级下)中移除。并在新的优先级下将事件插入到等待队列中[L4.15(12)]。任务也有可能正在等待延时的期满(参看第五章-任务管理)或是被挂起(参看4.07,挂起任务,OSTaskSuspend())。在这些情况下,从L4.15(8)到L4.15(12)这几行可以略过。
接着,OSTaskChangePrio()将指向任务OS_TCB的指针存到OSTCBPrioTbl[]中[L4.15(13)]。新的优先级被保存在OS_TCB中[L4.15(14)],重新计算的值也被保存在OS_TCB中[L4.15(15)]。OSTaskChangePrio()完成了关键性的步骤后,在新的优先级高于旧的优先级或新的优先级高于调用本函数的任务的优先级情况下,任务调度程序就会被调用[L4.15(16)]。
程序清单 L 4.15 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) || (1) |
newprio >= OS_LOWEST_PRIO) { |
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) |
bity = OSMapTbl[y]; |
x = newprio & 0x07; |
bitx = OSMapTbl[x]; |
OS_ENTER_CRITICAL(); |
if (oldprio == OS_PRIO_SELF) { (5) |
oldprio = OSTCBCur->OSTCBPrio; |
} |
if ((ptcb = OSTCBPrioTbl[oldprio]) != (OS_TCB *)0) { (6) |
OSTCBPrioTbl[oldprio] = (OS_TCB *)0; (7) |
if (OSRdyTbl[ptcb->OSTCBY] & ptcb->OSTCBBitX) { (8) |
if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {(9) |
OSRdyGrp &= ~ptcb->OSTCBBitY; |
} |
OSRdyGrp |= bity; (10) |
OSRdyTbl[y] |= bitx; |
} else { |
if ((pevent = ptcb->OSTCBEventPtr) != (OS_EVENT *)0) { (11) |
if ((pevent->OSEventTbl[ptcb->OSTCBY] &= |
pevent->OSEventGrp &= ~ptcb->OSTCBBitY; |
} |
pevent->OSEventGrp |= bity; (12) |
pevent->OSEventTbl[y] |= bitx; |
} |
} |
OSTCBPrioTbl[newprio] = ptcb; (13) |
ptcb->OSTCBPrio = newprio; (14) |
ptcb->OSTCBY = y; (15) |
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) |
OS_EXIT_CRITICAL(); |
return (OS_PRIO_ERR); |
} |
} |
} |
4.2 挂起任务,OSTaskSuspend()
有时候将任务挂起是很有用的。挂起任务可通过调用OSTaskSuspend()函数来完成。被挂起的任务只能通过调用OSTaskResume()函数来恢复。任务挂起是一个附加功能。也就是说,如果任务在被挂起的同时也在等待延时的期满,那么,挂起操作需要被取消,而任务继续等待延时期满,并转入就绪状态。任务可以挂起自己或者其它任务。
OSTaskSuspend()函数的代码如程序清单 L4.16所示。通常OSTaskSuspend()需要检验临界条件。首先,OSTaskSuspend()要确保用户的应用程序不是在挂起空闲任务[L4.16(1)],接着确认用户指定优先级是有效的[L4.16(2)]。记住最大的有效的优先级数(即最低的优先级)是OS_LOWEST_PRIO。注意,用户可以挂起统计任务(statistic)。可能用户已经注意到了,第一个测试[L4.16(1)]在[L4.16(2)]中被重复了。笔者这样做是为了能与µC/OS兼容。第一个测试能够被移除并可以节省一点程序处理的时间,但是,这样做的意义不大,所以笔者决定留下它。
接着,OSTaskSuspend()检验用户是否通过指定OS_PRIO_SELF来挂起调用本函数的任务本身[L4.16(3)]。用户也可以通过指定优先级来挂起调用本函数的任务[L4.16(4)]。在这两种情况下,任务调度程序都需要被调用。这就是笔者为什么要定义局部变量self的原因,该变量在适当的情况下会被测试。如果用户没有挂起调用本函数的任务,OSTaskSuspend()就没有必要运行任务调度程序,因为正在挂起的是较低优先级的任务。
然后,OSTaskSuspend()检验要挂起的任务是否存在[L4.16(5)]。如果该任务存在的话,它就会从就绪表中被移除[L4.16(6)]。注意要被挂起的任务有可能没有在就绪表中,因为它有可能在等待事件的发生或延时的期满。在这种情况下,要被挂起的任务在OSRdyTbl[]中对应的位已被清除了(即为0)。再次清除该位,要比先检验该位是否被清除了再在它没被清除时清除它快得多,所以笔者没有检验该位而直接清除它。现在,OSTaskSuspend()就可以在任务的OS_TCB中设置OS_STAT_SUSPEND标志了,以表明任务正在被挂起[L4.16(7)]。最后,OSTaskSuspend()只有在被挂起的任务是调用本函数的任务本身的情况下才调用任务调度程序[L4.16(8)]。
程序清单 L 4.16 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) |
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_EXIT_CRITICAL(); |
if (self == TRUE) { (8) |
OSSched(); |
} |
return (OS_NO_ERR); |
} |
} |
4.3 恢复任务,OSTaskResume()
在上一节中曾提到过,被挂起的任务只有通过调用OSTaskResume()才能恢复。OSTaskResume()函数的代码如程序清单 L4.17所示。因为OSTaskSuspend()不能挂起空闲任务,所以必须得确认用户的应用程序不是在恢复空闲任务[L4.17(1)]。注意,这个测试也可以确保用户不是在恢复优先级为OS_PRIO_SELF的任务(OS_PRIO_SELF被定义为0xFF,它总是比OS_LOWEST_PRIO大)。
要恢复的任务必须是存在的,因为用户要需要操作它的任务控制块OS_TCB[L4.17(2)],并且该任务必须是被挂起的[L4.17(3)]。OSTaskResume()是通过清除OSTCBStat域中的OS_STAT_SUSPEND位来取消挂起的[L4.17(4)]。要使任务处于就绪状态,OS_TCBDly域必须为0[L4.17(5)],这是因为在OSTCBStat中没有任何标志表明任务正在等待延时的期满。只有当以上两个条件都满足的时候,任务才处于就绪状态[L4.17(6)]。最后,任务调度程序会检查被恢复的任务拥有的优先级是否比调用本函数的任务的优先级高[L4.17(7)]。
程序清单 L 4.17 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_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) |
(ptcb->OSTCBDly == 0)) { (5) |
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); |
} |
} |
} |
4.4 获得有关任务的信息,OSTaskQuery()
用户的应用程序可以通过调用OSTaskQuery()来获得自身或其它应用任务的信息。实际上,OSTaskQuery()获得的是对应任务的OS_TCB中内容的拷贝。用户能访问的OS_TCB的数据域的多少决定于用户的应用程序的配置(参看OS_CFG.H)。由于µC/OS-Ⅱ是可裁剪的,它只包括那些用户的应用程序所要求的属性和功能。
要调用OSTaskQuery(),如程序清单 L4.18中所示的那样,用户的应用程序必须要为OS_TCB分配存储空间。这个OS_TCB与µC/OS-Ⅱ分配的OS_TCB是完全不同的数据空间。在调用了OSTaskQuery()后,这个OS_TCB包含了对应任务的OS_TCB的副本。用户必须十分小心地处理OS_TCB中指向其它OS_TCB的指针(即OSTCBNext与OSTCBPrev);用户不要试图去改变这些指针!一般来说,本函数只用来了解任务正在干什么——本函数是有用的调试工具。
程序清单 L 4.18 得到任务的信息 |
OS_TCB MyTaskData; |
|
void MyTask (void *pdata) |
{ |
pdata = pdata; |
for (;;) { |
/* 用户代码 */ |
err = OSTaskQuery(10, &MyTaskData); |
/* Examine error code .. */ |
/* 用户代码 */ |
} |
} |
OSTaskQuery()的代码如程序清单 L4.19所示。注意,笔者允许用户查询所有的任务,包括空闲任务[L4.19(1)]。用户尤其需要注意的是不要改变OSTCBNext与OSTCBPrev的指向。通常,OSTaskQuery()需要检验用户是否想知道当前任务的有关信息[L4.19(2)]以及该任务是否已经建立了[L4.19(3)]。所有的域是通过赋值语句一次性复制的而不是一个域一个域地复制的[L4.19(4)]。这样复制会比较快一点,因为编译器大多都能够产生内存拷贝指令。
程序清单 L 4.19 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); |
} |