freeRTOS源码解析4--tasks.c 5
4.2.13 继续任务--vTaskResume
接口:
void vTaskResume( TaskHandle_t xTaskToResume )
形参1:xTaskToResume ,想要继续的任务handle;
首先是vTaskResume调用的一个内部函数:static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ),用于检查任务是否是挂起状态,只有挂起的任务才能继续,否则是等待事件、通知等处于阻塞态,那就不能放到就绪列表,必须继续等待。这个接口逻辑非常简单,就直接看代码即可。
1 static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) 2 { 3 BaseType_t xReturn = pdFALSE; 4 const TCB_t * const pxTCB = xTask; 5 6 /* Accesses xPendingReadyList so must be called from a critical 7 * section. */ 8 9 /* It does not make sense to check if the calling task is suspended. */ 10 configASSERT( xTask ); 11 12 /* Is the task being resumed actually in the suspended list? */ 13 /* 检查任务是否在挂起列表, 不在则说明任务未挂起 */ 14 if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE ) 15 { 16 /* Has the task already been resumed from within an ISR? */ 17 /* 任务在挂起列表里, 但在调度器暂停时被移至等待就绪列表中, 则说明任务未挂起 */ 18 if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) 19 { 20 /* Is it in the suspended list because it is in the Suspended 21 * state, or because it is blocked with no timeout? */ 22 /* 任务在挂起列表里, 但在等待事件, 则说明任务未挂起 */ 23 if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) 24 { 25 #if ( configUSE_TASK_NOTIFICATIONS == 1 ) 26 { 27 BaseType_t x; 28 29 /* The task does not appear on the event list item of 30 * and of the RTOS objects, but could still be in the 31 * blocked state if it is waiting on its notification 32 * rather than waiting on an object. If not, is 33 * suspended. */ 34 /* 任务在挂起列表里, 且未等待事件, 那么如果在等待通知则未挂起, 否则挂起 */ 35 xReturn = pdTRUE; 36 37 for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) 38 { 39 if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) 40 { 41 xReturn = pdFALSE; 42 break; 43 } 44 } 45 } 46 #else /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ 47 { 48 xReturn = pdTRUE; 49 } 50 #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ 51 } 52 else 53 { 54 mtCOVERAGE_TEST_MARKER(); 55 } 56 } 57 else 58 { 59 mtCOVERAGE_TEST_MARKER(); 60 } 61 } 62 else 63 { 64 mtCOVERAGE_TEST_MARKER(); 65 } 66 67 /* 总结下来就是任务若在挂起列表里, 如果不是在等待事件或通知, 则是挂起状态, 否则不是 */ 68 69 return xReturn; 70 }
1 void vTaskResume( TaskHandle_t xTaskToResume ) 2 { 3 TCB_t * const pxTCB = xTaskToResume; 4 5 /* It does not make sense to resume the calling task. */ 6 configASSERT( xTaskToResume ); 7 8 #if ( configNUMBER_OF_CORES == 1 ) 9 /* The parameter cannot be NULL as it is impossible to resume the 10 * currently executing task. */ 11 if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) ) 12 #endif 13 { 14 taskENTER_CRITICAL(); 15 { 16 if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) 17 { 18 /* The ready list can be accessed even if the scheduler is 19 * suspended because this is inside a critical section. */ 20 /* 只有任务处于挂起状态才能继续 */ 21 ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); 22 prvAddTaskToReadyList( pxTCB ); 23 24 /* This yield may not cause the task just resumed to run, 25 * but will leave the lists in the correct state for the 26 * next yield. */ 27 /* 保证各任务列表处于正常的状态 */ 28 taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB ); 29 } 30 else 31 { 32 mtCOVERAGE_TEST_MARKER(); 33 } 34 } 35 taskEXIT_CRITICAL(); 36 } 37 else 38 { 39 mtCOVERAGE_TEST_MARKER(); 40 } 41 }
4.2.14 启动调度--vTaskStartScheduler
这个接口会创建空闲任务和软定时器任务,之后就是允许第一个任务,这个接口永远也不会返回,也是main中调用的最后一个接口。
这里补充一下,下面代码注释里说的禁止中断,在启动任务的时候会自动使能中断,是因为任务栈中最底层的第一个值就是XPSR的值为0x01000000,这里把xpsr里的primask清0了,就是表示使能中断。
这在《Cortex M3与M4权威指南》中描述XPSR寄存器的章节下有说明。
1 void vTaskStartScheduler( void ) 2 { 3 BaseType_t xReturn; 4 5 /* 创建空闲任务 */ 6 xReturn = prvCreateIdleTasks(); 7 8 #if ( configUSE_TIMERS == 1 ) 9 { 10 if( xReturn == pdPASS ) 11 { 12 xReturn = xTimerCreateTimerTask(); // 创建软定时器任务 13 } 14 else 15 { 16 mtCOVERAGE_TEST_MARKER(); 17 } 18 } 19 #endif /* configUSE_TIMERS */ 20 21 if( xReturn == pdPASS ) 22 { 23 /* Interrupts are turned off here, to ensure a tick does not occur 24 * before or during the call to xPortStartScheduler(). The stacks of 25 * the created tasks contain a status word with interrupts switched on 26 * so interrupts will automatically get re-enabled when the first task 27 * starts to run. */ 28 /* 关闭中断, 保证在调用xPortStartScheduler前不会发生tick中断, 所有创建 29 * 的任务的栈中都包含一个中断开启的状态字, 所以第一个任务运行时, 中断 30 * 会自动开启 */ 31 /* 暂时没看懂这个注释的意思, 但这个接口只是设置了中断优先级屏蔽寄存器, 32 * 实际的中断并未关闭, 系统中断被屏蔽了 */ 33 portDISABLE_INTERRUPTS(); 34 35 xNextTaskUnblockTime = portMAX_DELAY; 36 xSchedulerRunning = pdTRUE; 37 xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; 38 39 /* If configGENERATE_RUN_TIME_STATS is defined then the following 40 * macro must be defined to configure the timer/counter used to generate 41 * the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS 42 * is set to 0 and the following line fails to build then ensure you do not 43 * have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your 44 * FreeRTOSConfig.h file. */ 45 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); 46 47 /* Setting up the timer tick is hardware specific and thus in the 48 * portable interface. */ 49 50 /* The return value for xPortStartScheduler is not required 51 * hence using a void datatype. */ 52 /* 真正的启动调度器, 即启动了第一个任务 */ 53 ( void ) xPortStartScheduler(); 54 55 /* In most cases, xPortStartScheduler() will not return. If it 56 * returns pdTRUE then there was not enough heap memory available 57 * to create either the Idle or the Timer task. If it returned 58 * pdFALSE, then the application called xTaskEndScheduler(). 59 * Most ports don't implement xTaskEndScheduler() as there is 60 * nothing to return to. */ 61 } 62 else 63 { 64 /* This line will only be reached if the kernel could not be started, 65 * because there was not enough FreeRTOS heap to create the idle task 66 * or the timer task. */ 67 configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ); 68 } 69 70 /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0, 71 * meaning xIdleTaskHandles are not used anywhere else. */ 72 ( void ) xIdleTaskHandles; 73 74 /* OpenOCD makes use of uxTopUsedPriority for thread debugging. Prevent uxTopUsedPriority 75 * from getting optimized out as it is no longer used by the kernel. */ 76 ( void ) uxTopUsedPriority; 77 }
4.2.15 停止调度--vTaskEndScheduler
这个接口和上一个相对,而且这个接口一定是在某个任务中调用的,因为系统不会主动停止调度器,而正常运行中始终只有某个任务和系统在运行,所以必然是某个任务主动调用的。
这里也是一样。
1 void vTaskEndScheduler( void ) 2 { 3 #if ( INCLUDE_vTaskDelete == 1 ) 4 { 5 BaseType_t xCoreID; 6 7 #if ( configUSE_TIMERS == 1 ) 8 { 9 /* Delete the timer task created by the kernel. */ 10 /* 删除软定时器任务 */ 11 vTaskDelete( xTimerGetTimerDaemonTaskHandle() ); 12 } 13 #endif /* #if ( configUSE_TIMERS == 1 ) */ 14 15 /* Delete Idle tasks created by the kernel.*/ 16 for( xCoreID = 0; xCoreID < ( BaseType_t ) configNUMBER_OF_CORES; xCoreID++ ) 17 { 18 vTaskDelete( xIdleTaskHandles[ xCoreID ] ); // 删除空闲任务 19 } 20 21 /* Idle task is responsible for reclaiming the resources of the tasks in 22 * xTasksWaitingTermination list. Since the idle task is now deleted and 23 * no longer going to run, we need to reclaim resources of all the tasks 24 * in the xTasksWaitingTermination list. */ 25 /* 本应由空闲任务回收待删除任务的资源, 但现在空闲任务被删除了, 就在这里处理 */ 26 prvCheckTasksWaitingTermination(); 27 } 28 #endif /* #if ( INCLUDE_vTaskDelete == 1 ) */ 29 30 /* Stop the scheduler interrupts and call the portable scheduler end 31 * routine so the original ISRs can be restored if necessary. The port 32 * layer must ensure interrupts enable bit is left in the correct state. */ 33 /* 这跟启动调度器时关中断一样的疑问 */ 34 portDISABLE_INTERRUPTS(); 35 xSchedulerRunning = pdFALSE; 36 37 /* This function must be called from a task and the application is 38 * responsible for deleting that task after the scheduler is stopped. */ 39 vPortEndScheduler(); 40 }
好了,下一篇讲xTaskAbortDelay和xTaskIncrementTick接口,这两个接口较为复杂。
下篇再见。