本章重点讲解空闲任务的建立过程。
任务建立函数定义如下:
1 INT8U OSTaskCreate (void (*task)(void *p_arg), 2 void *p_arg, 3 OS_STK *ptos, 4 INT8U prio) 5 { 6 OS_STK *psp; 7 INT8U err; 8 #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ 9 OS_CPU_SR cpu_sr = 0u; 10 #endif 11 12 13 14 #ifdef OS_SAFETY_CRITICAL_IEC61508 15 if (OSSafetyCriticalStartFlag == OS_TRUE) { 16 OS_SAFETY_CRITICAL_EXCEPTION(); 17 } 18 #endif 19 20 #if OS_ARG_CHK_EN > 0u 21 if (prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable range */ 22 return (OS_ERR_PRIO_INVALID); 23 } 24 #endif 25 OS_ENTER_CRITICAL(); 26 if (OSIntNesting > 0u) { /* Make sure we don‘t create the task from within an ISR */ 27 OS_EXIT_CRITICAL(); 28 return (OS_ERR_TASK_CREATE_ISR); 29 } 30 if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn‘t already exist at this priority */ 31 OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ... */ 32 /* ... the same thing until task is created. */ 33 OS_EXIT_CRITICAL(); 34 psp = OSTaskStkInit(task, p_arg, ptos, 0u); /* Initialize the task‘s stack */ 35 err = OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0, 0u); 36 if (err == OS_ERR_NONE) { 37 if (OSRunning == OS_TRUE) { /* Find highest priority task if multitasking has started */ 38 OS_Sched(); 39 } 40 } else { 41 OS_ENTER_CRITICAL(); 42 OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others */ 43 OS_EXIT_CRITICAL(); 44 } 45 return (err); 46 } 47 OS_EXIT_CRITICAL(); 48 return (OS_ERR_PRIO_EXIST); 49 }
21~23行判断我们传递进来的优先级是否满足限定条件(不大于63),如果不满足,直接退出。
26~29行判断当前系统的状态,变量OSIntNesting的意义之前讲过,如果它大于0,那就代表目前处于中断服务函数中,在中断中是不允许建立新任务的。
30行首先判断任务是否已经存在,如果已经存在则跳出,数组OSTCBPrioTbl[prio]的元素是优先级,内容是任务的相关信息,如果该任务已建立,那么其内容必然不等于0.
31行当任务不存在时,首先随便赋个值给这个任务的管理数组,相当于是在这个元素上做个标志,告诉系统这个位置要保留下来不允许别人动,直到这个任务真正建立成功。
34行所调用的函数是初始化任务的堆栈,我们详细看看内部的处理,到底是如何进行初始化的:
函数OSTaskStkInit定义如下:
1 OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt) 2 { 3 OS_STK *stk; 4 5 6 (void)opt; /* ‘opt‘ is not used, prevent warning */ 7 stk = ptos; /* Load stack pointer */ 8 9 /* Registers stacked as if auto-saved on exception */ 10 *(stk) = (INT32U)0x01000000L; /* xPSR */ 11 *(--stk) = (INT32U)task; /* Entry Point */ 12 *(--stk) = (INT32U)0xFFFFFFFEL; /* R14 (LR) (init value will cause fault if ever used)*/ 13 *(--stk) = (INT32U)0x12121212L; /* R12 */ 14 *(--stk) = (INT32U)0x03030303L; /* R3 */ 15 *(--stk) = (INT32U)0x02020202L; /* R2 */ 16 *(--stk) = (INT32U)0x01010101L; /* R1 */ 17 *(--stk) = (INT32U)p_arg; /* R0 : argument */ 18 19 /* Remaining registers saved on process stack */ 20 *(--stk) = (INT32U)0x11111111L; /* R11 */ 21 *(--stk) = (INT32U)0x10101010L; /* R10 */ 22 *(--stk) = (INT32U)0x09090909L; /* R9 */ 23 *(--stk) = (INT32U)0x08080808L; /* R8 */ 24 *(--stk) = (INT32U)0x07070707L; /* R7 */ 25 *(--stk) = (INT32U)0x06060606L; /* R6 */ 26 *(--stk) = (INT32U)0x05050505L; /* R5 */ 27 *(--stk) = (INT32U)0x04040404L; /* R4 */ 28 29 return (stk); 30 }
这个函数该怎么解释呢?
它其实并不是真正的去初始化了CPU的栈空间,只是对一个模拟栈进行了初始化。
做过单片机程序的同学都很清楚,在程运行汇总遇到中断发生,必须要跳进中断中运行,但在跳进中断服务函数之前,它必须要做一些处理,比如保存现场,将当前的数据和寄存器值押入栈中,然后在去执行中断函数,因为只有这样做了在执行完中断函数以后才能找回原点继续执行剩下的代码。
单片机裸机程序是单线程,代码的执行逻辑始终处于一条时间线,就算是发生了中断嵌套,但在某一个具体的时间点,它也是单线的,只需要保存一个现场就可以回到原点,。
但在操作系统中,系统中同时执行了多个任务,每个任务之间都有可能发生切换,任务与任务之间不存在调用的关系,是相对独立的存在,任务的这个切换可以理解为单片机的中断,而且切换的顺序并不是线性的,一旦在任务过多的情况下,如果依然使用CPU自己的栈空间,那这样就会导致栈空间不够用,而且非常不方便。
所以系统创建了一个数组来模拟栈空间,比如这个空闲任务,我们跟踪它的参数可以知道:
OS_EXT OS_STK OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE]; /* Idle task stack */
#define OS_TASK_IDLE_STK_SIZE 128u /* Idle task stack size (# of OS_STK wide entries) */
系统定义了一个128byte的数组来模拟空闲任务的栈空间,专门用来存在和这个任务有关的数据,当我们从空闲任务跳出去的时候,就会把相应的信息保存在里面,等需要跳回的时候,就会从里面取出相应的信息,功能和CPU的栈完全一样。
原文地址:https://www.cnblogs.com/han-bing/p/9025847.html