- UID
- 1029342
- 性别
- 男
|
OSTaskCreateExt ()一开始先检测分配给任务的优先级是否有效[L4.3(1)]。任务的优先级必须在0到OS_LOWEST_PRIO之间。接着, OSTaskCreateExt()要确保在规定的优先级上还没有建立任务[L4.3(2)]。在使用µC/OS-Ⅱ时,每个任务都有特定的优先级。如果 某个优先级是空闲的,µC/OS-Ⅱ通过放置一个非空指针在OSTCBPrioTbl[]中来保留该优先级[L4.3(3)]。这就使得 OSTaskCreateExt()在设置任务数据结构的其他部分时能重新允许中断[L4.3(4)]。
为了对任务的堆栈进行检验[参看 4.03,堆栈检验,OSTaskStkChk()],用户必须在opt参数中设置OS_TASK_OPT_STK_CHK标志。堆栈检验还要求在任务建 立时堆栈的存储内容都是0(即堆栈已被清零)。为了在任务建立的时候将堆栈清零,需要在opt参数中设置OS_TASK_OPT_STK_CLR。当以上 两个标志都被设置好后,OSTaskCreateExt()才能将堆栈清零[L4.3(5)]。
接着,OSTaskCreateExt()调用 OSTaskStkInit()[L4.3(6)],它负责建立任务的堆栈。该函数是与处理器的硬件体系相关的函数,可以在OS_CPU_C.C文件中找 到。有关实现OSTaskStkInit()的细节可参看第八章——移植µC/OS-Ⅱ。如果已经有人在你用的处理器上成功地移植了µC/OS-Ⅱ,而你 又得到了他的代码,就不必考虑该函数的实现细节了。OSTaskStkInit()函数返回新的堆栈栈顶(psp),并被保存在任务的0S_TCB中。
µC/OS -Ⅱ支持的处理器的堆栈既可以从上(高地址)往下(低地址)递减也可以从下往上递增(参看4.02,任务堆栈)。用户在调用 OSTaskCreateExt()的时候必须知道堆栈是递增的还是递减的(参看用户所用处理器的OS_CPU.H中的 OS_STACK_GROWTH),因为用户必须得把堆栈的栈顶传递给OSTaskCreateExt(),而栈顶可能是堆栈的最低地址(当 OS_STK_GROWTH为0时),也可能是最高地址(当OS_STK_GROWTH为1时)。
一旦OSTaskStkInit()函数完成 了建立堆栈的任务,OSTaskCreateExt()就调用OSTCBInit() [L4.3(7)],从空闲的OS_TCB缓冲池中获得并初始化一个OS_TCB。OSTCBInit()的代码在OSTaskCreate()中曾描述 过(参看4.00节), 从OSTCBInit()返回后,OSTaskCreateExt()要检验返回代码[L4.3(8)],如果成功,就增加OSTaskCtr[L4.3 (9)],OSTaskCtr用于保存产生的任务数目。如果OSTCBInit()返回失败,就置OSTCBPrioTbl[prio]的入口为0 [L4.3(13)]以放弃对该任务优先级的占用。然后,OSTaskCreateExt()调用OSTaskCreateHook()[L4.3 (10)],OSTaskCreateHook()是用户自己定义的函数,用来扩展OSTaskCreateExt()的功能。 OSTaskCreateHook()可以在OS_CPU_C.C中定义(如果OS_CPU_HOOKS_EN置1),也可以在其它地方定义(如果 OS_CPU_HOOKS_EN置0)。注意,OSTaskCreateExt()在调用OSTaskCreateHook()时,中断是关掉的,所以用 户应该使OSTaskCreateHook()函数中的代码尽量简化,因为这将直接影响中断的响应时间。OSTaskCreateHook()被调用时会 收到指向任务被建立时的OS_TCB的指针。这意味着该函数可以访问OS_TCB数据结构中的所有成员。
如果OSTaskCreateExt ()函数是在某个任务的执行过程中被调用的(即OSRunning置为True[L4.3(11)]),以任务调度函数会被调用[L4.3(12)]来判 断是否新建立的任务比原来的任务有更高的优先级。如果新任务的优先级更高,内核会进行一次从旧任务到新任务的任务切换。如果在多任务调度开始之前(即用户 还没有调用OSStart()),新任务就已经建立了,则任务调度函数不会被调用。
任务堆栈
每个任务都有自己的堆栈空间。堆栈必须声明为OS_STK类型,并且由连续的内存空间组成。用户可以静态分配堆栈空间(在编译的时候分配)也可以动态地分配堆栈空间(在运行的时候分配)。静态堆栈声明如程序清单 L4.4和4.5所示,这两种声明应放置在函数的外面。
程序清单 L4.4 静态堆栈
static OS_STK MyTaskStack[stack_size];
或
程序清单 L4.5 静态堆栈
OS_STK MyTaskStack[stack_size];
用户可以用C编译器提供的malloc()函数来动态地分配堆栈空间,如程序清单 L4.6所示。在动态分配中,用户要时刻注意内存碎片问题。特别是当用户反复地建立和删除任务时,内存堆中可能会出现大量的内存碎片,导致没有足够大的一 块连续内存区域可用作任务堆栈,这时malloc()便无法成功地为任务分配堆栈空间。
程序清单 L L4.6 用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()是非常可行的。
图 F4.1 内存碎片
µC/OS-Ⅱ支持的处理器的堆栈既可以从上(高地址)往下(低地址) 长也可以从下往上长(参看4.02,任务堆栈)。用户在调用OSTaskCreate()或OSTaskCreateExt()的时候必须知道堆栈是怎样 长的,因为用户必须得把堆栈的栈顶传递给以上两个函数,当OS_CPU.H文件中的OS_STK_GROWTH置为0时,用户需要将堆栈的最低内存地址传 递给任务创建函数,如程序清单4.7所示。
程序清单 L4.7 堆栈从下往上递增
OS_STK TaskStack[TASK_STACK_SIZE];
OSTaskCreate(task, pdata, &TaskStack[0], prio);
当OS_CPU.H文件中的OS_STK_GROWTH置为1时,用户需要将堆栈的最高内存地址传递给任务创建函数,如程序清单4.8所示。
程序清单 L4.8 堆栈从上往下递减
OS_STK TaskStack[TASK_STACK_SIZE];
OSTaskCreate(task, pdata, &TaskStack[TASK_STACK_SIZE-1], prio);
这个问题会影响代码的可移植性。如果用户想将代码从支持往下递减堆栈的处理器中移植到支持往上递增堆栈的处理器中的话,用户得使代码同时适应以上两种情况。在这种特殊情况下,程序清单 L4.7和4.8可重新写成如程序清单 L4.9所示的形式。
程序清单 L 4.9 对两个方向增长的堆栈都提供支持
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寄存器。
堆栈检验,OSTaskStkChk()
有时候决定任务实际所需的堆栈空间大小是很有必要的。因为这样用户就可以避免为任务分配过多的堆栈空间,从而减少自己的应用程序代码所需的RAM(内存)数量。µC/OS-Ⅱ提供的OSTaskStkChk()函数可以为用户提供这种有价值的信息。
在图4.2中,笔者假定堆栈是从上往下递减的(即OS_STK_GROWTH被置为1),但以下的讨论也同样适用于从下往上长的堆栈[F4.2(1)]。 µC/OS-Ⅱ是通过查看堆栈本身的内容来决定堆栈的方向的。只有内核或是任务发出堆栈检验的命令时,堆栈检验才会被执行,它不会自动地去不断检验任务的 堆栈使用情况。在堆栈检验时,µC/OS-Ⅱ要求在任务建立的时候堆栈中存储的必须是0值(即堆栈被清零)[F4.2(2)]。另外,µC/OS-Ⅱ还需 要知道堆栈栈底(BOS)的位置和分配给任务的堆栈的大小[F4.2(2)]。在任务建立的时候,BOS的位置及堆栈的这两个值储存在任务的OS_TCB 中。
为了使用µ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()的参数并调用之。 |
|