crying

crying

0个粉丝

41

问答

0

专栏

21

资料

crying  发布于  2008-08-02 16:59:19
采纳率 0%
41个问答
4999

uc/os的任务堆栈问题--堆栈生长方向

静态堆栈

OS_STK  MyTaskStack[stack_size];



    用户可以用C编译器提供的malloc()函数来动态地分配堆栈空间,如程序清单 L4.6所示。在动态分配中,用户要时刻注意内存碎片问题。特别是当用户反复地建立和删除任务时,内存堆中可能会出现大量的内存碎片,导致没有足够大的一块连续内存区域可用作任务堆栈,这时malloc()便无法成功地为任务分配堆栈空间。

用malloc()为任务分配堆栈空间

OS_STK  *pstk;





pstk = (OS_STK *)malloc(stack_size);

if (pstk != (OS_STK *)0) {            /* 确认malloc()能得到足够地内存空间 */

    Create the task;

}




    图4.1表示了一块能被malloc()动态分配的3K字节的内存堆 [F4.1(1)]。为了讨论问题方便,假定用户要建立三个任务(任务A,B和C),每个任务需要1K字节的空间。设第一个1K字节给任务A, 第二个1K字节给任务B, 第三个1K字节给任务C[F4.1(2)]。然后,用户的应用程序删除任务A和任务C,用free()函数释放内存到内存堆中[F4.1(3)]。现在,用户的内存堆虽有2K字节的自由内存空间,但它是不连续的,所以用户不能建立另一个需要2K字节内存的任务(即任务D)。如果用户并不会去删除任务,使用malloc()是非常可行的。

堆栈从下往上递增

OS_STK  TaskStack[TASK_STACK_SIZE];



OSTaskCreate(task, pdata, &TaskStack[0], prio);

堆栈从上往下递减

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寄存器。
堆栈检验函数

INT8U OSTaskStkChk (INT8U prio, OS_STK_DATA *pdata)

{

    OS_TCB  *ptcb;

    OS_STK  *pchk;

    INT32U   free;

    INT32U   size;





    pdata->OSFree = 0;

    pdata->OSUsed = 0;

    if (prio > OS_LOWEST_PRIO && prio != OS_PRIO_SELF) {

        return (OS_PRIO_INVALID);

    }

    OS_ENTER_CRITICAL();

    if (prio == OS_PRIO_SELF) {                                              (1)

        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)

        OS_EXIT_CRITICAL();

        return (OS_TASK_OPT_ERR);

    }

    free = 0;                                                                  (4)

    size = ptcb->OSTCBStkSize;

    pchk = ptcb->OSTCBStkBottom;

    OS_EXIT_CRITICAL();

#if OS_STK_GROWTH == 1

    while (*pchk++ == 0) {

        free++;

    }

#else

    while (*pchk-- == 0) {

        free++;

    }

#endif

    pdata->OSFree = free * sizeof(OS_STK);                                 (5)

    pdata->OSUsed = (size - free) * sizeof(OS_STK);

    return (OS_NO_ERR);

删除任务,OSTaskDel()
    有时候删除任务是很有必要的。删除任务,是说任务将返回并处于休眠状态(参看3.02,任务状态),并不是说任务的代码被删除了,只是任务的代码不再被µC/OS-Ⅱ调用。通过调用OSTaskDel()就可以完成删除任务的功能(如程序清单 L4.11所示)。OSTaskDel()一开始应确保用户所要删除的任务并非是空闲任务,因为删除空闲任务是不允许的[L4.11(1)]。不过,用户可以删除statistic任务[L4.11(2)]。接着,OSTaskDel()还应确保用户不是在ISR例程中去试图删除一个任务,因为这也是不被允许的[L4.11(3)]。调用此函数的任务可以通过指定OS_PRIO_SELF参数来删除自己[L4.11(4)]。接下来OSTaskDel()会保证被删除的任务是确实存在的[L4.11(3)]。如果指定的参数是OS_PRIO_SELF的话,这一判断过程(任务是否存在)自然是可以通过的,但笔者不准备为这种情况单独写一段代码,因为这样只会增加代码并延长程序的执行时间。



程序清单 L 4.11   删除任务

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)

        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)]。
我来回答
回答0个
时间排序
认可量排序
易百纳技术社区暂无数据
或将文件直接拖到这里
悬赏:
E币
网盘
* 网盘链接:
* 提取码:
悬赏:
E币

Markdown 语法

  • 加粗**内容**
  • 斜体*内容*
  • 删除线~~内容~~
  • 引用> 引用内容
  • 代码`代码`
  • 代码块```编程语言↵代码```
  • 链接[链接标题](url)
  • 无序列表- 内容
  • 有序列表1. 内容
  • 缩进内容
  • 图片![alt](url)
+ 添加网盘链接/附件

Markdown 语法

  • 加粗**内容**
  • 斜体*内容*
  • 删除线~~内容~~
  • 引用> 引用内容
  • 代码`代码`
  • 代码块```编程语言↵代码```
  • 链接[链接标题](url)
  • 无序列表- 内容
  • 有序列表1. 内容
  • 缩进内容
  • 图片![alt](url)
举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

易百纳技术社区