1、任务什么时候会被删除?
一开始,任务在操作系统中是以函数代码的形式存在的,在操作系统启动的时候被加载到内存中,并未运行。并且,最开始的时候就绪表和就绪组是空的,或者说里面的内容都是0.很明显,这时候任务在内存中睡眠,处于睡眠态。如果不调用任务创建函数对任务进行操作,该任务将永远处于睡眠态直到操作系统结束运行,被清除出内存。
(好像没有正面回答这个问题……)
2、任务创建过程的回顾
任务创建的过程:首先分配一个空闲的TCB给任务,然后对该TCB的各个域进行赋值,对任务的堆栈进行初始化,其中,任务的代码的地址被压入堆栈。这为以后任务的运行做了充分的准备。就绪表和就绪组做了适当的处理,根据任务的优先级进行了设置。就绪TCB链表也插入了该TCB。
而任务删除,便是任务创建的逆过程。
3、任务删除
任务删除(func: OSTaskDel() ),便是将就绪表、就绪组进行逆向操作,就绪链表中的相关TCB应该被移除,转移到空闲TCB链表。和任务创建一样,也要进行一些检查,看任务是否符合被删除的条件。
此外,任务删除还涉及一个请求删除(func: OSTaskDelReq() )的问题,因此任务删除看似简单,实际上是比较复杂的一个过程。
所以,这一节将会提到两个函数。
我发现之前一个很不好的习惯,不同文章上流程图和代码的顺序不一样。以后统一先放流程图,再上代码好了。
4、任务删除函数:OSTaskDel()
流程图:
挺长的,预示着这个函数并不算简单。
上代码:
1 #if OS_TASK_DEL_EN > 0 2 INT8U OSTaskDel (INT8U prio) 3 { 4 #if (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) 5 OS_FLAG_NODE *pnode; 6 #endif 7 8 OS_TCB *ptcb; 9 10 #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ 11 OS_CPU_SR cpu_sr = 0; 12 #endif 13 14 if (OSIntNesting > 0) { /* See if trying to delete from ISR */ 15 return (OS_ERR_TASK_DEL_ISR); 16 } 17 if (prio == OS_TASK_IDLE_PRIO) { /* Not allowed to delete idle task */ 18 return (OS_ERR_TASK_DEL_IDLE); 19 } 20 #if OS_ARG_CHK_EN > 0 21 if (prio >= OS_LOWEST_PRIO) { /* Task priority valid ? */ 22 if (prio != OS_PRIO_SELF) { 23 return (OS_ERR_PRIO_INVALID); 24 } 25 } 26 #endif
line 7定义了一个任务控制块指针;
之后便是一些参数的检查,注意line 22的OS_PRIO_SELF,为自身的优先级。当任务删除自身的时候,可使用
OSTaskDel (OS_RPIO_SELF) //#define OS_PRIO_SELF 0XFFu
之后便是包围在OS_ENTER_CRITICAL()与 OS_EXIT_CRITICAL()之间的内容,即开始访问全局变量。
1 OS_ENTER_CRITICAL(); 2 if (prio == OS_PRIO_SELF) { /* See if requesting to delete self */ 3 prio = OSTCBCur->OSTCBPrio; /* Set priority to delete to current */ 4 } 5 ptcb = OSTCBPrioTbl[prio]; 6 if (ptcb == (OS_TCB *)0) { /* Task to delete must exist */ 7 OS_EXIT_CRITICAL(); 8 return (OS_ERR_TASK_NOT_EXIST); 9 } 10 if (ptcb == OS_TCB_RESERVED) { /* Must not be assigned to Mutex */ 11 OS_EXIT_CRITICAL(); 12 return (OS_ERR_TASK_DEL); 13 }
进入临界区,所以把全局变量中的OSTCBCur(当前任务)的优先级赋值给prio,把对应的地址传递给ptcb(已用红色标出)。
至此参数已经检查完毕。之后便是根据各种标志,来选择性地对任务状态进行改变。
1 OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX; 2 if (OSRdyTbl[ptcb->OSTCBY] == 0) { /* Make task not ready */ 3 OSRdyGrp &= ~ptcb->OSTCBBitY; 4 } 5 6 #if (OS_EVENT_EN) 7 if (ptcb->OSTCBEventPtr != (OS_EVENT *)0) { 8 OS_EventTaskRemove(ptcb, ptcb->OSTCBEventPtr); /* Remove this task from any event wait list */ 9 } 10 #if (OS_EVENT_MULTI_EN > 0) 11 if (ptcb->OSTCBEventMultiPtr != (OS_EVENT **)0) { /* Remove this task from any events' wait lists*/ 12 OS_EventTaskRemoveMulti(ptcb, ptcb->OSTCBEventMultiPtr); 13 } 14 #endif 15 #endif 16 17 #if (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) 18 pnode = ptcb->OSTCBFlagNode; 19 if (pnode != (OS_FLAG_NODE *)0) { /* If task is waiting on event flag */ 20 OS_FlagUnlink(pnode); /* Remove from wait list */ 21 } 22 #endif
line 1~ line 4,是清除该任务的就绪标志;
line 6~line 15,是清除任务等待事件的标志(get out of the line, I mean);
line 17~line 22,是与事件标志组管理有关,未学到……
1 ptcb->OSTCBDly = 0; /* Prevent OSTimeTick() from updating */ 2 ptcb->OSTCBStat = OS_STAT_RDY; /* Prevent task from being resumed */ 3 ptcb->OSTCBStatPend = OS_STAT_PEND_OK; 4 if (OSLockNesting < 255u) { /* Make sure we don't context switch */ 5 OSLockNesting++; 6 } 7 OS_EXIT_CRITICAL(); /* Enabling INT. ignores next instruc. */
line 2表示。该任务的state为OS_STAT_RDY, 并非表示该任务已经就绪了,而是去掉了诸如等待标志,实际上任务被删除了,将处于睡眠态。
line4和line5,强行将调度器加上一次锁, 我不知道什么调度器,回过头来解释(1、为什么上锁;2、在哪里将该值减1)。
1 OS_Dummy(); /* ... Dummy ensures that INTs will be */ 2 OS_ENTER_CRITICAL(); /* ... disabled HERE! */ 3 if (OSLockNesting > 0) { /* Remove context switch lock */ 4 OSLockNesting--; 5 } 6 OSTaskDelHook(ptcb); /* Call user defined hook */ 7 OSTaskCtr--; /* One less task being managed */ 8 OSTCBPrioTbl[prio] = (OS_TCB *)0; /* Clear old priority entry */ 9 if (ptcb->OSTCBPrev == (OS_TCB *)0) { /* Remove from TCB chain */ 10 ptcb->OSTCBNext->OSTCBPrev = (OS_TCB *)0; 11 OSTCBList = ptcb->OSTCBNext; 12 } else { 13 ptcb->OSTCBPrev->OSTCBNext = ptcb->OSTCBNext; 14 ptcb->OSTCBNext->OSTCBPrev = ptcb->OSTCBPrev; 15 } 16 ptcb->OSTCBNext = OSTCBFreeList; /* Return TCB to free TCB list */ 17 OSTCBFreeList = ptcb; 18 #if OS_TASK_NAME_SIZE > 1 19 ptcb->OSTCBTaskName[0] = '?'; /* Unknown name */ 20 ptcb->OSTCBTaskName[1] = OS_ASCII_NUL; 21 #endif 22 OS_EXIT_CRITICAL();
OS_Dummy()只是一个空函数,换句话说,在喘了一口气之后,函数又关了中断。
先把调度器减1?
之后便是一系列的收官动作——OSTaskCtr减1,把这个任务从就绪表摘下插入空闲链表中。
最后开中断。
1 if (OSRunning == OS_TRUE) { 2 OS_Sched(); /* Find new highest priority task */ 3 } 4 return (OS_ERR_NONE);
此设计任务调度,回过头解释。
至此,任务已删除完。由于任务删除的代码很长,而执行过程中一直在访问全局变量,因此使系统不能响应中断——而作为一个RTOS,不能及时响应中断,那真是对不起它的招牌,于是,在整个函数的中间,又开了一次中断。
5、请求删除任务函数:OSTaskDelReq()
上流程图:
相对于第一个函数,OSTaskDelReq()看起来简单很多。
代码:
1 #if OS_TASK_DEL_EN > 0 2 INT8U OSTaskDelReq (INT8U prio) 3 { 4 INT8U stat; 5 OS_TCB *ptcb; 6 #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ 7 OS_CPU_SR cpu_sr = 0; 8 #endif 9 10 11 12 if (prio == OS_TASK_IDLE_PRIO) { /* Not allowed to delete idle task */ 13 return (OS_ERR_TASK_DEL_IDLE); 14 } 15 #if OS_ARG_CHK_EN > 0 16 if (prio >= OS_LOWEST_PRIO) { /* Task priority valid ? */ 17 if (prio != OS_PRIO_SELF) { 18 return (OS_ERR_PRIO_INVALID); 19 } 20 } 21 #endif 22 if (prio == OS_PRIO_SELF) { /* See if a task is requesting to ... */ 23 OS_ENTER_CRITICAL(); /* ... this task to delete itself */ 24 stat = OSTCBCur->OSTCBDelReq; /* Return request status to caller */ 25 OS_EXIT_CRITICAL(); 26 return (stat); 27 }
惯例,刚刚开始依然是参数检查。
line 22中检查是否有任务删除自己。如果有,则返回任务控制块中OSTCBDelReq域中的参数,即是否有删除请求。
1 OS_ENTER_CRITICAL(); 2 ptcb = OSTCBPrioTbl[prio]; 3 if (ptcb == (OS_TCB *)0) { /* Task to delete must exist */ 4 OS_EXIT_CRITICAL(); 5 return (OS_ERR_TASK_NOT_EXIST); /* Task must already be deleted */ 6 } 7 if (ptcb == OS_TCB_RESERVED) { /* Must NOT be assigned to a Mutex */ 8 OS_EXIT_CRITICAL(); 9 return (OS_ERR_TASK_DEL); 10 } 11 ptcb->OSTCBDelReq = OS_ERR_TASK_DEL_REQ; /* Set flag indicating task to be DEL. */ 12 OS_EXIT_CRITICAL(); 13 return (OS_ERR_NONE); 14 } 15 #endif
后半部分的代码根据参数不同,分别执行判断是否有请求参数标志和给对方任务打上请求删除标志。
6、OSTaskDel() 和 OSTaskDelReq()
很潦草地分析完这两个代码,现在谈谈为什么要有两个删除函数,或者,为什么要有请求删除任务函数。
有时候,任务会占用一些内存缓冲区或信号量一类的资源,这时假如另一个任务试图删除该任务,这些被占用的资源就会因为没有释放而丢失,造成内存泄露。
就像在C中,强调,malloc动态分配内存后,一定要使用free释放一样。
OSTaskDelReq名称虽然是请求,却是集请求与响应于一段代码内,该代码的功能是请求删除某任务和查看是否有任务要删除自己。
所以,完整的删除流程是:两个任务一起合作,一个提出删除要求,另外一个在循环里面,首先执行删除要求判断,是否有其他任务要求删除它,是的话就马上释放资源,调
用删除任务函数。提出删除要求的任务,就是简单的设置删除任务的一个域。被删除的任务就是检查那个域。
两个函数合作删除。
7、链接
最后放一个链接,作者自己把工程改了下,然后进行调试。我目前还没这水平:ucos任务删除