本文屏蔽了大量的技术细节,只是简单的阐述内核启动的实现。由于本人才疏学浅知识有限,如有错误,还请各位不吝赐教。
从任务调度开始
void vTaskStartScheduler( void ) /* 这个函数创建了idle任务,并且创建软件定时器任务 ,到最后调用xPortStartScheduler */
看看xPortStartScheduler 函数做了什么
1 BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ 2 { 3 /* Make PendSV, CallSV and SysTick the same priority as the kernel. */ 4 5 /* 设置PendSV和Systick中断优先级为最低,这里是操作寄存器直接设置,为什么要设置最低,因为任务切换就是在PendSV中运行,为了让其他中断能够及时的运行,为什么要在PendSV中切换任务,因为PendSV可以挂起延迟执行,具体参考Cortex-M3 权威指南 第7章异常SVC和PendSV */ 6 portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI; 7 8 portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; 9 10 #if ( configENABLE_MPU == 1 ) 11 { 12 /* Setup the Memory Protection Unit (MPU). */ 13 prvSetupMPU(); 14 } 15 #endif /* configENABLE_MPU */ 16 17 /* Start the timer that generates the tick ISR. Interrupts are disabled 18 * here already. */ 19 vPortSetupTimerInterrupt(); 20 21 /* Initialize the critical nesting count ready for the first task. */ 22 ulCriticalNesting = 0; /* 此变量表示临界段嵌套次数,可以表示是否在中断里 */ 23 24 /* Start the first task. */ 25 vStartFirstTask(); 26 27 /* Should never get here as the tasks will now be executing. Call the task 28 * exit error function to prevent compiler warnings about a static function 29 * not being called in the case that the application writer overrides this 30 * functionality by defining configTASK_RETURN_ADDRESS. Call 31 * vTaskSwitchContext() so link time optimization does not remove the 32 * symbol. */ 33 vTaskSwitchContext(); /* 查找最高的优先级 */ 34 prvTaskExitError(); 35 36 /* Should not get here. */ 37 return 0; 38 } 39
taskSELECT_HIGHEST_PRIORITY_TASK 函数实现了优先级的查找,提供了两种办法,这里只研究一种
1 #define taskSELECT_HIGHEST_PRIORITY_TASK() 2 { 3 UBaseType_t uxTopPriority = uxTopReadyPriority; 4 5 /* Find the highest priority queue that contains ready tasks. */ 6 while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) 7 { 8 configASSERT( uxTopPriority ); 9 --uxTopPriority; 10 } 11 12 /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of 13 * the same priority get an equal share of the processor time. */ 14 listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); 15 uxTopReadyPriority = uxTopPriority; 16 } /* taskSELECT_HIGHEST_PRIORITY_TASK */
寻找最高的优先级,更新pxCurrentTCB
while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )
表示就绪表中是否有任务。没有任务就下标减一继续找,因为下标就是优先级,直到找到了优先级。
如果跳出while,表示找到了最高优先级的任务。有任务表示最高的优先级,因为下标越小,优先级越低。
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) )
找到的列表项更新到当前Tcb.
uxTopReadyPriority = uxTopPriority;
更新最高的就绪优先级
while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )
表示就绪表中是否有任务。没有任务就下标减一继续找,因为下标就是优先级,直到找到了优先级。
如果跳出while,表示找到了最高优先级的任务。有任务表示最高的优先级,因为下标越小,优先级越低。
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) )
找到的列表项更新到当前Tcb.
uxTopReadyPriority = uxTopPriority;
更新最高的就绪优先级
pxReadyTasksLists里面存放的是Tcb(准确来说应该是存放Tcb里面的 xStateListItem 列表项)
第二种是使用前导零指令CLZ,意思是32个bit表示优先级,哪个位是0就减掉。
参考野火教程的一句话: 任务控制块里面有一个 xStateListItem 成员, 数据类型为 ListItem_t, 我们将任务插入
到就绪列表里面,就是通过将任务控制块的 xStateListItem 这个节点插入到就绪列表中来实
现的。如果把就绪列表比作是晾衣架, 任务是衣服,那 xStateListItem 就是晾衣架上面的钩
子,每个任务都自带晾衣架钩子,就是为了把自己挂在各种不同的链表中 。
xPortPendSVHandler,次函数进行任务切换。