⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter4.txt

📁 ucos的PDF书籍、2.00源码以及我的学习心得
💻 TXT
字号:
4.00 Creating a Task, OSTaskCreate()
只想说一处:
OS_ENTER_CRITICAL();
if(OSTCBPrioTbl[prio] == (OS_TCB *)0){
    OSTCBPrioTbl[prio] = (OS_TCB *)1;
    OS_EXIT_CRITICAL();
     ....
这里的OSTCBPrioTbl[prio] = (OS_TCB *)1;一句仅仅是为了尽快将OSTCBPrioTbl[prio]填充为一个非0的值,以表示该优先级已经被使用了,从而防止其他任务也使用这一优先级。但为什么要用(OS_TCB *)1而不是TCB的实际指针值呢?这是因为,如果要使用TCB实际的指针值,需要依次调用OSTaskStkInit和OSTCBInit两个函数。这两个函数都是很花时间的,如果在禁止中断的情况下使用它们,势必会造成很高的中断延时。为了能够尽早地使程序能够恢复中断同时又保证当前使用优先级不被其他任务使用,作者在这里使用(OS_TCB *)1作为标志。在将OSTCBPrioTbl[prio]项填写为(OS_TCB *)1后即可恢复中断,而同时因为OSTCBPrioTbl[prio]的值已不再为(OS_TCB *)0,故其他任务也无法使用该优先级,一举两得。

4.02 Task Staks
对Task Stack的建立有两种方式:静态方式和动态方式。
静态方式实际上就是将Task Stack声明为全局变量。这样在程序编译时,就将空间分配好了,而且该空间不会被回收,知道程序运行结束。
动态方式可以使用C语言中的malloc函数在程序运行时对地址空间进行分配,而在该空间不再需要时,使用free将其回收。
从以上讨论来看,貌似使用动态方式要优越许多,其实也不尽然。动态方式可能会造成内存碎片,从而影响内存的进一步使用。而且,如果程序中存在任务需要多次建立删除,建议使用静态方式为其申请堆栈空间。因为若使用动态方式的话,需要反复为该任务分配和回收内存空间,增加系统的开销。

4.04 Deleting a Task, OSTaskDel()
删除一个任务需要以下步骤:
1、对输入的优先级进行例行的界限检查;
2、将任务从就绪队列中移除,同时如果任务在等待某事件将其从等待队列中移除;
3、将任务PCB中的OSTCBDly项赋值为0,防止任务被时间中断处理函数OSTimeTick重新加入就绪队列;
4、将任务PCB中的OSTCBStat项赋值为OS_STAT_RDY,防止任务被其他函数(如:OSTaskResume)重新加入就绪队列;
5、至此为止,如果任务被切换的话,它将无法被再次调回,但此时对任务的删除操作尚未完成需要继续。不过,恼火的是,如果继续在关闭中断的情况下操作势必会造成很大的中断延时。为了减少中断延时同时保证任务不被切换,在恢复中断前,需要关闭任务调度。这项操作不需要太长的时间,只需要被延时的中断能够保证响应就可以了。因此,这里作者引入了一个空函数OSdummy,执行此函数仅仅是为了提供几个指令周期的时间供被延时的中断响应;
6、进行一些善后的处理包括减少OSTaskCtr的值、将任务使用的优先级释放以及将任务TCB从当前使用TCB链表中移除并加入空闲PCB链表中;
7、调用OSShed函数。

4.05 Requesting to delete a task, OSTaskDelReq()
如果一个任务在执行中会控制某些资源,那么在删除该任务时,需要首先将这些资源释放。
为了达到以上目的,需要删除操作的请求者与被删除任务中都引入对OSTaskDelReq函数的呼叫。两类任务的具体代码流程如下:
删除操作的请求者:
void RequestorTask (void *pdata)
{
    INT8U err;
    pdata = pdata;
    for (;;) {
        /* Application code */
        if (‘TaskToBeDeleted()’ needs to be deleted) { 
            while (OSTaskDelReq(TASK_TO_DEL_PRIO) != OS_TASK_NOT_EXIST) { 
                OSTimeDly(1); 
            }
        }
        /* Application code */ 
    }
}

待删除任务:
void TaskToBeDeleted (void *pdata)
{
    INT8U err;
    pdata = pdata;
    for (;;) {
        /* Application code */
        if (OSTaskDelReq(OS_PRIO_SELF) == OS_TASK_DEL_REQ) { 
            Release any owned resources; 
            De-allocate any dynamic memory;
            OSTaskDel(OS_PRIO_SELF); 
        } else {
            /* Application code */
        }
    }
}
具体原理如下:
在任务的TCB中设有一名为OSTCBDelReq的变量,此变量在任务建立时被初始化为OS_NO_ERR。同时,对于OSTaskDelReq函数,如果输入参数为OS_PRIO_SELF则只返回当前任务TCB中OSTCBDelReq的值;而如果输入参数是一个有效的优先级值,则判断具有此优先级的任务是否存在,若存在将该任务的OSTCBDelReq项赋值为OS_TASK_DEL_REQ并返回OS_NO_ERR,否则返回OS_TASK_NOT_EXIST。
根据以上讨论,在RequestorTask函数尚未呼叫OSTaskDelReq(TASK_TO_DEL_PRIO)时,待删除任务中OSTaskDelReq(OS_PRIO_SELF)的返回值为OS_NO_ERR,因此其执行else中的代码进行正常的操作。而一旦RequestorTask呼叫了OSTaskDelReq(TASK_TO_DEL_PRIO),待删除任务TCB中的OSTCBDelReq被赋值为OS_TASK_DEL_REQ,因此当再次执行TaskToBeDeleted时,将执行if内的语句,即释放资源删除自己。
经过上述讨论,可以发现,这里OSTCBDelReq实际相当于一个邮箱的角色用于RequestorTask 与TaskToBeDeleted 之间的通信。
另外,需要说明的是,实际上也可以由待删除任务通过调用OSTaskDelReq函数完成对自己的删除。
在这种情况下,仅需要一个任务函数即TaskToBeDeleted ,但其代码结构需要做一定的调整:
void TaskToBeDeleted (void *pdata)
{
    INT8U err;
    pdata = pdata;
    for (;;) {
        /* Application code */
        if (OSTaskDelReq(OS_PRIO_SELF) == OS_TASK_DEL_REQ) { 
            Release any owned resources; 
            De-allocate any dynamic memory;
            OSTaskDel(OS_PRIO_SELF); 
        } else {
            /* Application code */
            if (‘TaskToBeDeleted()’ needs to be deleted){
                while (OSTaskDelReq(PRIO_OF_CUR_TASK) != OS_TASK_NOT_EXIST) {
                     OSTimeDly(1); 
                 }
            }
            /* Application code */
        }
    }
}
最后,建议一点,这种形式的任务删除主要用于需要掌握某些资源的任务。对于一般任务,使用此种结构也可以正常完成目标,不过这相当于将简单的问题复杂化了,因此对于一般的任务的删除操作还是使用普通的代码结构要好一些。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -