当前位置:   article > 正文

手把手教你FreeRTOS源码详解(二)——任务管理_freertos代码解读

freertos代码解读

FreeRTOS源码解析集合(全网最详细)
手把手教你FreeRTOS源码解析(一)——内存管理
手把手教你FreeRTOS源码详解(二)——任务管理
手把手教你FreeRTOS源码详解(三)——队列
手把手教你FreeRTOS源码详解(四)——信号量、互斥量、递归互斥量

1、任务创建

1.1 任务控制块TCB

在FreeRTOS中,每个任务都有一个属于自己的任务控制块,方便对任务进行管理,TCB结构体源码如下(已经删去了一些条件编译):

typedef struct tskTaskControlBlock
{
	volatile StackType_t	*pxTopOfStack;	/*·指向任务栈顶*/
	ListItem_t			xStateListItem;		/* 表示任务状态的链表(就绪, 阻塞, 挂起 )*/
	ListItem_t			xEventListItem;		/* 指向事件链表中的某一任务*/
	UBaseType_t			uxPriority;			/*任务的优先级,0为最低优先级 */
	StackType_t			*pxStack;			/*指向栈的起始地址 */
	char				pcTaskName[ configMAX_TASK_NAME_LEN ];/*保存任务名称*/
} tskTCB;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

1.2 任务创建xTaskCreate

首先我们来看一下任务创建函数xTaskCreate的参数。

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
							const char * const pcName,
							const uint16_t usStackDepth,
							void * const pvParameters,
							UBaseType_t uxPriority,
							TaskHandle_t * const pxCreatedTask )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

pxTaskCode:函数指针,指向要执行的函数
pcName:任务的名称
usStackDepth:任务栈大小,注意单位为字(4个字节)
pvParameters:传递给任务函数的参数
uxPriority:任务的优先级
pxCreatedTask :任务句柄,用于管理任务

在FreeRTOS中,很多项目是根据堆栈的增长方向来的配置的,这使FreeRTOS有了更好的兼容性。堆栈的增长方向有向下增长向上增长两种。
向下增长:高地址向低地址增长。
向上增长:低地址向高地址增长。

在这里插入图片描述
在任务创建之初,需要分别给TCB任务控制块和任务分配内存空间,对于堆栈增长方向的不同,给TCB任务控制块和任务分配内存空间的顺序也有所差异。
源码如下:

#if( portSTACK_GROWTH > 0 )
		{
			/* 为TCB结构体申请内存堆  */
			pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

			if( pxNewTCB != NULL )
			{
				/* 为任务申请内存堆 */
				pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

				if( pxNewTCB->pxStack == NULL )
				{
					/* Could not allocate the stack.  Delete the allocated TCB. */
					vPortFree( pxNewTCB );
					pxNewTCB = NULL;
				}
			}
		}
		/*栈向下增长*/
		#else /* portSTACK_GROWTH */
		{
		StackType_t *pxStack;

			/* 为任务申请内存堆 */
			pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

			if( pxStack != NULL )
			{
				/* 为TCB结构体申请内存堆 */
				pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */

				if( pxNewTCB != NULL )
				{
					/* 用TCB结构体的成员变量来指向任务内存堆 */
					pxNewTCB->pxStack = pxStack;
				}
				else
				{
					/* 由于TCB结构体创建失败,因此任务堆不能使用,释放任务内存堆 */
					vPortFree( pxStack );
				}
			}
			else
			{
				pxNewTCB = NULL;
			}
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

portSTACK_GROWTH>0表示堆栈向上增长,先申请TCB任务控制块的堆栈空间,再申请任务堆栈;反之,如果堆栈向下增长,先申请任务堆栈,再申请TCB任务控制块的堆栈空间。按这样的顺序申请堆栈空间,堆栈的扩展就不会覆盖掉TCB任务控制块的内容,Cortex-M采用的是向下增长
以向下增长为例,若先申请TCB任务控制块堆栈空间,再申请任务堆栈(谁先申请堆栈谁的地址更低):
在这里插入图片描述
这样任务堆栈的扩展可能会覆盖掉TCB区域,若先申请任务堆栈,再申请TCB任务控制块的堆栈空间:
在这里插入图片描述
任务控制块就没有被覆盖的危险了。堆栈向上增长也类似。

堆栈申请完成,判断任务控制块内存是否申请成功(申请失败返回NULL)

if( pxNewTCB != NULL )
  • 1

如果是动态创建的任务,则进行标记,便于后面删除任务

#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
			{
				/*将任务标记为动态创建,以便于后面删除任务 */
				pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
			}
  • 1
  • 2
  • 3
  • 4
  • 5

初始化新任务以及将任务添加至就绪列表–后续讲解

prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
prvAddNewTaskToReadyList( pxNewTCB );
  • 1
  • 2

1.3 初始化任务prvInitialiseNewTask

首先计算出任务栈顶地址(堆栈增长方向不同计算方法不同),再进行地址对齐。
若堆栈向下增长(STM32采用这种增长方式):

#if( portSTACK_GROWTH < 0 )
{
	/*计算栈顶地址*/
	pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
	/*将地址作8字节对齐--&(~0x0007)*/
	pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type. */

	/* Check the alignment of the calculated top of stack is correct. */
	configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

pxNewTCB->pxStack为任务块堆栈的起始地址,pxTopOfStack 指向当前任务块堆栈的栈顶。
在这里插入图片描述

若堆栈向上增长

/* 栈向上增长 */
#else /* portSTACK_GROWTH */
{
	/*指向任务栈顶*/
	pxTopOfStack = pxNewTCB->pxStack;
	/* Check the alignment of the stack buffer is correct. */
	configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

	/* 指向任务栈底 */
	pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

pxTopOfStack 指向当前任务块堆栈的栈顶,pxNewTCB->pxEndOfStack指向任务块的栈底。
在这里插入图片描述
用for循环将输入的任务名赋值给TCB结构体的成员变量pcTaskName

/* 在TCB结构体中保存任务的名字 */
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
	pxNewTCB->pcTaskName[ x ] = pcName[ x ];

	/* 自己定义的任务名字是不定长的,若定义的任务名短于configMAX_TASK_NAME_LEN,则赋值完(检测到0)就跳出该循环 */
	if( pcName[ x ] == 0x00 )
	{
		break;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

最后在任务名的结尾加上字符串结束标识符’\0’

pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
  • 1

检测任务优先级是否过大,若超过最高优先级,则置为最高优先级,任务的最高优先级为configMAX_PRIORITIES -1,如configMAX_PRIORITIES 为5时,优先级可取0,1,2,3,4

if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
	uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else
{
	mtCOVERAGE_TEST_MARKER();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

将任务优先级赋值给TCB结构体

pxNewTCB->uxPriority = uxPriority;
  • 1

初始化TCB结构体中的任务状态表、事件表

vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
  • 1
  • 2

设置TCB控制块状态表的成员变量pvOwner,是属于pxNewTCB的

listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
  • 1

设置TCB控制块任务表成员变量xItemValue的值

listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
  • 1

设置TCB控制块任务表的成员变量pvOwner,是属于pxNewTCB的

listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
  • 1

如果存在任务句柄(输入的任务句柄不为NULL),则将任务句柄指向任务控制块

	if( ( void * ) pxCreatedTask != NULL )
{
	/*	将任务句柄指向任务控制块TCB */
	*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
  • 1
  • 2
  • 3
  • 4
  • 5

1.4 添加新任务至就序链表prvAddNewTaskToReadyList

先进入临界区,防止中断打断就绪链表更新
taskENTER_CRITICAL为最强的临界保护,直接屏蔽了中断,使用时应尽量保证临界区较小一些
vTaskSuspendAll仅仅挂起了任务调度器,即关闭了任务调度器,防止任务之间的资源抢夺

taskENTER_CRITICAL();
  • 1

当前任务数加一

uxCurrentNumberOfTasks++;
  • 1

当前没有任务,或任务均被挂起,则将该任务设置为将执行的任务,并且如果是第一次创建任务,则需要初始化任务状态链表

	if( pxCurrentTCB == NULL )
	{
		/* 如果没有其它任务,或者其它任务均被挂起 - 将该任务设置为将执行的任务 */
		pxCurrentTCB = pxNewTCB;
		/*第一次创建任务*/
		if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
		{
			/* 第一次创建任务,初始化任务链表 */
			prvInitialiseTaskLists();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

如果不是第一次创建任务,且任务调度器未执行,该任务为优先级最高的任务,则将其设置为当前任务

		if( xSchedulerRunning == pdFALSE )
		{
			if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
			{
				pxCurrentTCB = pxNewTCB;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

任务数加一,该任务数用作任务的编号

	uxTaskNumber++;
	#if ( configUSE_TRACE_FACILITY == 1 )
	{
		/* 初始化TCB任务控制块编号 */
		pxNewTCB->uxTCBNumber = uxTaskNumber;
	}
	#endif /* configUSE_TRACE_FACILITY */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

将任务添加至就绪列表

prvAddTaskToReadyList( pxNewTCB )
  • 1

prvAddTaskToReadyList( pxNewTCB )实际调用的函数是vListInsertEnd,即采用尾插法,在链表的尾部插入元素

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
  • 1

pxList:列表项要插入的列表
pxNewListItem :要插入的列表项

若任务调度正在执行,创建的任务优先级高于正在执行的任务,则正在执行的任务“让步”于新任务

if( xSchedulerRunning != pdFALSE )
{
	/* 若创建的任务优先级高于正在执行的任务,则正在执行的任务“让步”于新任务 */
	if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
	{
		taskYIELD_IF_USING_PREEMPTION();
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

1.5 初始化任务链表prvInitialiseTaskLists

在FreeRTOS中,一个任务有多种状态,每种状态对应一个链表,将任务置于不同的状态,实质上就是将任务添加至对应的状态链表。
我们首先来看一下链表初始化函数vListInitialise( List_t * const pxList )
pxIndex表示列表项的索引号,初始状态时,链表中只有xListEnd一个元素,因此pxIndex指向xListEnd

pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );	
  • 1

在链表中,按照xItemValue升序排列的,xItemValue越小代表优先级越高
将 pxList->xListEnd.xItemValue设置为最大,保证xListEnd始终在链表的最后位置

pxList->xListEnd.xItemValue = portMAX_DELAY;
  • 1

任务链表为双向链表,对其进行初始化

pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );	
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
  • 1
  • 2

uxNumberOfItems记录链表中元素的个数,刚初试化链表,元素个数为0

pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
  • 1

分析完链表初始化函数后,我们回到任务链表初始化函数prvInitialiseTaskLists
不同优先级的就绪任务,阻塞任务,挂起任务以及已经删除但为释放内存的任务都有对应的链表。
遍历初始化不同优先级的就绪任务链表

for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
{
	vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
}
  • 1
  • 2
  • 3
  • 4

初始化阻塞任务链表

vListInitialise( &xDelayedTaskList1 );
vListInitialise( &xDelayedTaskList2 );
  • 1
  • 2

初始化就绪任务过渡链表,此时调度器关闭,一些任务进入就绪状态,但是任务还未放入就绪链表中,等待任务调度器开始,进行一次新的调度

vListInitialise( &xPendingReadyList );
  • 1

初始化任务删除链表

vListInitialise( &xTasksWaitingTermination );
  • 1

初始化挂起任务链表

vListInitialise( &xSuspendedTaskList );
  • 1

2、任务删除

同样,删除任务首先进入临界区。

taskENTER_CRITICAL();
  • 1

如果输入句柄为NULL,则获取当前运行任务的句柄,否则pxTCB就为输入句柄

pxTCB = prvGetTCBFromHandle( xTaskToDelete );
  • 1

prvGetTCBFromHandle实质如下:

( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) pxCurrentTCB : ( TCB_t * ) ( pxHandle ) )
  • 1

将任务从链表中删除,若删除后链表中的任务数为0,则清除相应就绪链表的就绪标志位

if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
		{
			taskRESET_READY_PRIORITY( pxTCB->uxPriority );
		}
  • 1
  • 2
  • 3
  • 4

任务是否在等待某个事件,如果是,则将其放置于相应的链表

if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
		{
			( void ) uxListRemove( &( pxTCB->xEventListItem ) );
		}
  • 1
  • 2
  • 3
  • 4

如果删除的任务为正在运行的任务,将需要删除的任务插入xTasksWaitingTermination中,在空闲函数里面来释放内存

			/*如果删除的任务为正在运行的任务*/
		if( pxTCB == pxCurrentTCB )
		{
			/* 将需要删除的任务插入xTasksWaitingTermination中,在空闲函数里面来释放内存*/
			vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );

			/* uxDeletedTasksWaitingCleanUp为删除链表xTasksWaitingTermination中的任务数 */
			++uxDeletedTasksWaitingCleanUp;

			/* 删除任务钩子函数,用户自己实现 */
			portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

若删除的任务不是正在运行的任务,则直接删除

else
		{
		/*正在运行的任务数减1*/
			--uxCurrentNumberOfTasks;
		/*	删除TCB结构体,释放堆栈和任务控制块内存*/
			prvDeleteTCB( pxTCB );

			/* 重置下一个任务唤醒时间,避免下一个唤醒的任务为当前删除的任务*/
			prvResetNextTaskUnblockTime();
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

退出临界区

taskEXIT_CRITICAL();
  • 1

如果删除的是正在运行的任务那么就需要强制进行一次任务切换。

		if( xSchedulerRunning != pdFALSE )
	{
		if( pxTCB == pxCurrentTCB )
		{
			configASSERT( uxSchedulerSuspended == 0 );
			/*强制进行任务切换*/
			portYIELD_WITHIN_API();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.1 链表项删除uxListRemove

首先获取需要删除的任务处于哪一个链表中

List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
  • 1

将任务从链表中删除,即将任务前后两个任务连接起来。

	pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
	pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
  • 1
  • 2

如果pxIndex指向要被删除的任务,则将pxIndex指向要被删除任务的前一项

if( pxList->pxIndex == pxItemToRemove )
{
	pxList->pxIndex = pxItemToRemove->pxPrevious;
}
  • 1
  • 2
  • 3
  • 4

将被被删除对象的成员变量pvContainer清空

	pxItemToRemove->pvContainer = NULL;
  • 1

删除后,链表中任务数减1

( pxList->uxNumberOfItems )--;
  • 1

最后返回链表中的任务数目

return pxList->uxNumberOfItems;
  • 1

3、延时函数vTaskDelay

延时函数首先判断延时时间是否大于0

if( xTicksToDelay > ( TickType_t ) 0U )
  • 1

挂起任务调度器

vTaskSuspendAll();
  • 1

挂起任务调度器实质就是将uxSchedulerSuspended加1,当uxSchedulerSuspended大于0时,即不会进行任务调度,当uxSchedulerSuspended为0时就会进行任务调度。

void vTaskSuspendAll( void )
{
	++uxSchedulerSuspended;
}
  • 1
  • 2
  • 3
  • 4

将需要延时的任务添加至延时列表

prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
  • 1

重新恢复任务调度器,并用xAlreadyYielded获取其返回值,若为pdFALSE则未进行任务调度,需要在后续进行任务调度

xAlreadyYielded = xTaskResumeAll();
  • 1

若未进行任务调度,在此处强制进行任务调度

	if( xAlreadyYielded == pdFALSE )
	{
		portYIELD_WITHIN_API();
	}
  • 1
  • 2
  • 3
  • 4

3.1 prvAddCurrentTaskToDelayedList

vTaskDelay函数的本质是调用prvAddCurrentTaskToDelayedList,将任务添加至对应的延时、阻塞链表,同样,首先将任务从就绪列表中移除,移除后并所移除的就绪列表中是否还有其余就绪的任务,若剩余就绪任务数为0,则清除该列表的就绪标志位

if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
	/* The current task must be in a ready list, so there is no need to
	check, and the port reset macro can be called directly. */
	portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里我们只考虑允许阻塞的情况,即宏INCLUDE_vTaskSuspend=1时
如果延时时间为最大,且允许阻塞时,直接将任务添加至阻塞列表中去,xCanBlockIndefinitely 为pdTRUE即为允许阻塞

	if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
	{
		/* Add the task to the suspended task list instead of a delayed task
		list to ensure it is not woken by a timing event.  It will block
		indefinitely. */
		vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如果阻塞时间不为最大时,首先计算出唤醒任务的时间,xConstTickCount为执行任务prvAddCurrentTaskToDelayedList的时间,xTicksToWait为延时时间,xTimeToWake为唤醒时间

xTimeToWake = xConstTickCount + xTicksToWait;
  • 1

将xTimeToWake写入任务列表的状态列表成员变量xItemValue中

listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
  • 1

xTimeToWake为TickType_t类型,为uint_16型,当xTimeToWake<xConstTickCount时,即发生了溢出,则将任务添加至溢出链表pxOverflowDelayedTaskList中

if( xTimeToWake < xConstTickCount )
			{
				/* Wake time has overflowed.  Place this item in the overflow
				list. */
				vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
			}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果没有发生溢出,则将任务添加至链表pxDelayedTaskList中

vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) )
  • 1

全局变量xNextTaskUnblockTime保存着下一个任务的唤醒时间,如果xTimeToWake<xNextTaskUnblockTime时,即有任务需更快的被唤醒,则更新xNextTaskUnblockTime为xTimeToWake

			if( xTimeToWake < xNextTaskUnblockTime )
			{
				xNextTaskUnblockTime = xTimeToWake;
			}
  • 1
  • 2
  • 3
  • 4
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/825844
推荐阅读
相关标签
  

闽ICP备14008679号