第1节:趣谈FreeRTOS--HR的一天

50 阅读11分钟

【原创声明】:本文系原创作品,谢绝任何形式的未经授权的转载。如需转载,请先私信联系我获取许可,谢谢合作!

引言

今天是星期一,为了和老李学习FreeRTOS,小鹤早早来到了公司。不一会儿,老李背着双肩包来到了工位。

👨 小鹤:
早啊!李哥

🧔 老李:
早!今天怎么来这么早啊?小鹤

👨 小鹤:
上周李哥说要用“互联网企业日常工作”情景剧的方式讲FreeRTOS,我早早就醒了,我们现在就开始吧!

🧔 老李:
哈哈!那我就先用比喻来介绍几个基础概念吧:

比喻FreeRTOS名词
员工 Task
保洁阿姨 IdleTask
项目经理 调度器
员工的公司档案 TCB
员工的TODO-List 调用栈(Stack)
员工的任务 TaskFunction

🧔 老李:
有了这几个基础概念,接下来我们就能在此基础上逐步拓展,最终勾勒出FreeRTOS的整个架构。

🧔 老李:
今天的故事从HR的一天开始😊


早上9点,HR小美准时进入公司,在茶水间打上一杯拿铁,回到工位,打开飞书日历:09:30 Android和服务端分别入职新员工小张和小刘14:00 和老王谈裁员补偿18:00 在海底捞参加Android组给小赵举行的送行宴

1764304955.png

🧔 老李:
我们可以认为HR是一个特殊的Task,她负责招聘(xTaskCreate) 和 员工离职(vTaskDelete),这里离职分两种如果vTaskDelete(NULL),代表调用这个函数的员工(Task主动离职;如果vTaskDelete传入员工(Task)名字,则代表裁员(删除)这个员工(Task),例如:vTaskDelete(laowang)

👨 小鹤:
HR这个特殊的Task是什么时候创建的呢?

🧔 老李:
当然是公司(FreeRTOS)成立的时候,对应是程序初始化的时候,可以是静态初始化xTaskCreateStatic

xTaskCreate

🧔 老李:
HR招聘员工,发工牌(TCB pxCreatedTask)、分配任务(TaskFunction_t pxTaskCode)、设置职级(UBaseType_t uxPriority)

关键步骤

  1. 分配TCB
  2. 分配Task栈
  3. 调用prvInitialiseNewTask初始化TCB
  4. 添加到就绪队列

参数:

TaskFunction_t pxTaskCode: Task的执行函数
char * const pcName: 任务的名字
configSTACK_DEPTH_TYPE usStackDepth: 栈的深度 uxStackDepth * sizeof(StackType_t)是栈的真正大小
void * pvParameters: 传入执行函数的参数列表
UBaseType_t uxPriority: 任务优先级
TaskHandle_t * pxCreatedTask: 创建的TCB,TaskHandle_ttskTCB的别名

BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,
                       const char * const pcName,
                       const configSTACK_DEPTH_TYPE usStackDepth,
                       void * const pvParameters,
                       UBaseType_t uxPriority,
                       TaskHandle_t * const pxCreatedTask )
{
	TCB_t *pxNewTCB;
	BaseType_t xReturn;

		#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 ) ) );

				if( pxNewTCB->pxStack == NULL )
				{
					/* 栈内存分配失败,回收TCB内存 */
					vPortFree( pxNewTCB );
					pxNewTCB = NULL;
				}
			}
		}
		#else /* Cortex-M3 系列是这个分支 portSTACK_GROWTH */
		{
		StackType_t *pxStack;

			/* 分配栈内存 */
			pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );

			if( pxStack != NULL )
			{
				/* 分配TCB内存 */
				pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 

				if( pxNewTCB != NULL )
				{
					pxNewTCB->pxStack = pxStack;
				}
				else
				{
					/* TCB内存分配失败,回收栈内存 */
					vPortFree( pxStack );
				}
			}
			else
			{
				pxNewTCB = NULL;
			}
		}
		#endif /* portSTACK_GROWTH */

		if( pxNewTCB != NULL )
		{
			#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */
			{
				/* 既 允许动态分配内存 又 允许静态分配内存,则在TCB中标明是xTaskCreate的Task而不是xTaskCreateStatic,用来在vTaskDelete(内部是prvDeleteTCB)中决定是否要释放内存 */
				pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
			}
			#endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */

			prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
			prvAddNewTaskToReadyList( pxNewTCB );
			xReturn = pdPASS;
		}
		else
		{
			xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
		}

		return xReturn;
	}

prvInitialiseNewTask

🧔 老李:
真正负责发工牌(TCB pxCreatedTask)、分配任务(TaskFunction_t pxTaskCode)、设置职级(UBaseType_t uxPriority)的函数

关键步骤

  1. 初始化 WaterMask [^1]
  2. 计算pxTopOfStackpxEndOfStack
  3. 复制Task name
  4. 设置TCB的优先级和基础优先级(优先级继承机制使用)
  5. 初始化xStateListItemxEventListItempxTaskTag[^2]、pvThreadLocalStoragePointers [^3]等
  6. 调用 pxPortInitialiseStack 将Task的函数地址放在PC的位置、pvParameters放在R0位置,当执行 ldmia sp!, {r3, r14} 时,Task的函数地址会自动赋值给PC从开开始从Task开始执行
  7. 将TCB赋值给 pxCreatedTask 从而在应用层可以获取到当前 TaskHandler_t(TCB)

参数:

TaskFunction_t pxTaskCode: Task的执行函数
char * pcName: 栈的名字
uint32_tulStackDepth: 栈的深度 uxStackDepth * sizeof(StackType_t)是栈的真正大小
void * pvParameters: 传入执行函数的参数列表
UBaseType_t uxPriority: 任务优先级
TaskHandle_t * pxCreatedTask: 创建的TCB,TaskHandle_t是tskTCB的别名
TCB_t * pxNewTCB: TCB
MemoryRegion_t * xRegions: MPU描述,为该任务定义受保护的内存区域


static void prvInitialiseNewTask(TaskFunction_t pxTaskCode,
                                 const char * const pcName,
                                 const uint32_t ulStackDepth,
                                 void * const pvParameters,
                                 UBaseType_t uxPriority,
                                 TaskHandle_t * const pxCreatedTask,
                                 TCB_t *pxNewTCB,
                                 const MemoryRegion_t * const xRegions )
{
StackType_t *pxTopOfStack;
UBaseType_t x;

	#if( portUSING_MPU_WRAPPERS == 1 ) /* 暂不考虑MPU分支 */
		/* Should the task be created in privileged mode? */
		BaseType_t xRunPrivileged;
		if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )
		{
			xRunPrivileged = pdTRUE;
		}
		else
		{
			xRunPrivileged = pdFALSE;
		}
		uxPriority &= ~portPRIVILEGE_BIT;
	#endif /* portUSING_MPU_WRAPPERS == 1 */

	/* Avoid dependency on memset() if it is not required. */
	#if( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
	{
		/* 用0xA5填满整个任务栈,当任务运行时0xA5会被修改成其他数值,这时从栈顶向栈底数出剩余0xA5的字节个数X,用Stack_size - X 即可算出运行中使用栈的上限 */
		( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
	}
	#endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */

	#if( portSTACK_GROWTH < 0 ) /* Cortex-M3 系列是这个分支 高地址向低地址增长  */
	{
		pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
           /* 将栈指针按字节对齐,以确保系统的稳定性和性能 */
		pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );

		configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

		#if( configRECORD_STACK_HIGH_ADDRESS == 1 )
		{
			pxNewTCB->pxEndOfStack = pxTopOfStack;
		}
		#endif /* configRECORD_STACK_HIGH_ADDRESS */
	}
	#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 ) );

		/* The other extreme of the stack space is required if stack checking is
		performed. */
		pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
	}
	#endif /* portSTACK_GROWTH */

	/* 将task name 存入TCB */
	if( pcName != NULL )
	{
		for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
		{
			pxNewTCB->pcTaskName[ x ] = pcName[ x ];

			/* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than
			configMAX_TASK_NAME_LEN characters just in case the memory after the
			string is not accessible (extremely unlikely). */
			if( pcName[ x ] == ( char ) 0x00 )
			{
				break;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}

		/* Ensure the name string is terminated in the case that the string length
		was greater or equal to configMAX_TASK_NAME_LEN. */
		pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
	}
	else
	{
		pxNewTCB->pcTaskName[ 0 ] = 0x00;
	}

	/* 优先级必须小于configMAX_PRIORITIES,否则将会被强制设置成configMAX_PRIORITIES - 1. */
	if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
	{
		uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	pxNewTCB->uxPriority = uxPriority;
	#if ( configUSE_MUTEXES == 1 )
	{
		pxNewTCB->uxBasePriority = uxPriority;
		pxNewTCB->uxMutexesHeld = 0;
	}
	#endif /* configUSE_MUTEXES */

   /* 初始化xStateList “状态链表项” 和xEventListItem list “事件链表项” */
	vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
	vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

	/* Set the pxNewTCB as a link back from the ListItem_t.  This is so we can get
	back to	the containing TCB from a generic item in a list. */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );

   /* 事件等待相关的列表列表是按照xItemValue递增排序的,确保高优先级任务排在前面。*/
	listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );

	#if ( portCRITICAL_NESTING_IN_TCB == 1 )
	{
		pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
	}
	#endif /* portCRITICAL_NESTING_IN_TCB */

   /* 初始化pxTaskTag,参见 注解2 */
	#if ( configUSE_APPLICATION_TASK_TAG == 1 )
	{
		pxNewTCB->pxTaskTag = NULL;
	}
	#endif /* configUSE_APPLICATION_TASK_TAG */

   /* 运行花费的CPU时间(节拍)清零 */
	#if ( configGENERATE_RUN_TIME_STATS == 1 )
	{
		pxNewTCB->ulRunTimeCounter = 0UL;
	}
	#endif /* configGENERATE_RUN_TIME_STATS */

	#if ( portUSING_MPU_WRAPPERS == 1 ) /* 暂不考虑MPU分支 */
	{
		vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth );
	}
	#else
	{
		/* Avoid compiler warning about unreferenced parameter. */
		( void ) xRegions;
	}
	#endif

	#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
	{
           /* 初始化Thread Local数组 */
		for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
		{
			pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL;
		}
	}
	#endif

   /* Notify 相关初始化 */
	#if ( configUSE_TASK_NOTIFICATIONS == 1 )
	{
		pxNewTCB->ulNotifiedValue = 0;
		pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
	}
	#endif

	#if ( configUSE_NEWLIB_REENTRANT == 1 )
	{
		/* Initialise this task's Newlib reent structure.
		See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
		for additional information. */
		_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
	}
	#endif

	#if( INCLUDE_xTaskAbortDelay == 1 )
	{
		pxNewTCB->ucDelayAborted = pdFALSE;
	}
	#endif

	/* 初始化任务的Stack */
	#if( portUSING_MPU_WRAPPERS == 1 ) /* 暂不考虑MPU分支 */
	{
		/* If the port has capability to detect stack overflow,
		pass the stack end address to the stack initialization
		function as well. */
		#if( portHAS_STACK_OVERFLOW_CHECKING == 1 )
		{
			#if( portSTACK_GROWTH < 0 ) /* Cortex-M3 系列是这个分支 高地址向低地址增长 */
			{
				pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged );
			}
			#else /* portSTACK_GROWTH */
			{
				pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged );
			}
			#endif /* portSTACK_GROWTH */
		}
		#else /* portHAS_STACK_OVERFLOW_CHECKING */
		{
			pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged );
		}
		#endif /* portHAS_STACK_OVERFLOW_CHECKING */
	}
	#else /* portUSING_MPU_WRAPPERS */
	{
		/* If the port has capability to detect stack overflow,
		pass the stack end address to the stack initialization
		function as well. */
		#if( portHAS_STACK_OVERFLOW_CHECKING == 1 )
		{
			#if( portSTACK_GROWTH < 0 )
			{
				pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters );
			}
			#else /* portSTACK_GROWTH */
			{
				pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters );
			}
			#endif /* portSTACK_GROWTH */
		}
		#else /* portHAS_STACK_OVERFLOW_CHECKING */
		{
			pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
		}
		#endif /* portHAS_STACK_OVERFLOW_CHECKING */
	}
	#endif /* portUSING_MPU_WRAPPERS */

	if( pxCreatedTask != NULL )
	{
		/* 返回TCB Handler */
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

[1] WaterMask 是一给非常巧妙的机制,用于判断当前Task使用了多少栈,防止栈size过小导致溢出。其原理是在Task执行前,将栈的内容全部初始化为一个特定的值(此处是 0xA5 ),当Task执行完成后,栈的内容会被修改,就像栈顶就像“水位线”一样,随着栈的使用会将0xA5修改为其他值,最后:

栈size - pxEndOfStack开始剩余0xA5的个数 = 栈的最大使用size

[2] pxTaskTag一个用户自定义的指针函数,类似Android View的tag,用户可以用Tag携带任意函数在Task的任意生命周期,只要有 TaskHandle_t 就可以调用该函数指针做 相关操作,这样增加了灵活性,同时不破坏TaskHandle_t核心结构 ,并降低耦合。

[3] pvThreadLocalStoragePointers “线程封闭”,当访问共享的可变数据时,通常需要使用同步。一种避免使用同步的方式就是不共享数据。类似Java的ThreadLocal

vTaskDelete

🧔 老李:
员工离职(vTaskDelete(NULL)) & HR裁员(vTaskDelete(laowang)) 😛

关键步骤

  1. 如果在 就绪listDelay list 中,则从 相关list 中移除
  2. 删除资源
  3. 让出CPU(调度器未运行)

参数:

TaskHandle_t xTaskToDelete: 需要删除的Task的TCB,如果xTaskToDelete == NULL,则说明是Task“自杀”。

#if ( INCLUDE_vTaskDelete == 1 )

    void vTaskDelete( TaskHandle_t xTaskToDelete ) 
    {
    TCB_t *pxTCB;

        taskENTER_CRITICAL();/* 进入临界区,暂停禁止任务切换与中断服务,确保关键代码执行的原子性 */
        {
            /* 如果xTaskToDelete == NULL则返回当前Task的TCB,也就是Task主动离职,否则返回xTaskToDelete */
            pxTCB = prvGetTCBFromHandle( xTaskToDelete );

            /*  从就绪list或Delay list中移除 */
            if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) 
            {
                /* 当前优先队列为空,则将当前优先级从就绪优先级位图(uxTopReadyPriority)中清除 */
                taskRESET_READY_PRIORITY( pxTCB->uxPriority ); 
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            /* 如果当前Task在事件等待队列中,则从相关队列中移除 */
            if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
            {
                ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            /* 类似PID */
            uxTaskNumber++;

            if( pxTCB == pxCurrentTCB )
            {
                /* 当任务“主动离职”的时候,不能在自己的执行上下文中完成删除自身资源,它会被加入到xTasksWaitingTermination僵尸Task列表中,等待清洁工阿姨IdleTask进行回收 */
                vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );

                /* 待清理任务数量+1,IdleTask会逐一清理直至uxDeletedTasksWaitingCleanUp为0 */
                ++uxDeletedTasksWaitingCleanUp;

                traceTASK_DELETE( pxTCB );

                /* The pre-delete hook is primarily for the Windows simulator,
                in which Windows specific clean up operations are performed,
                after which it is not possible to yield away from this task -
                hence xYieldPending is used to latch that a context switch is
                required. */
                portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
            }
            else
            {
                /* “裁员模式”HR会帮助员工(Task)做好“善后”工作 */
                --uxCurrentNumberOfTasks;
                traceTASK_DELETE( pxTCB );
                /* 删除TCB */
                prvDeleteTCB( pxTCB );

                /* 计算下一个Delay任务解除阻塞的时间 */
                prvResetNextTaskUnblockTime();
            }
        }
        taskEXIT_CRITICAL(); /* 退出临界区 */

        /* 调度器没有在运行 */
        if( xSchedulerRunning != pdFALSE )
        {
            if( pxTCB == pxCurrentTCB )
            {
                configASSERT( uxSchedulerSuspended == 0 );
                portYIELD_WITHIN_API(); /* 调度器没有运行,而且当前Task是“主动离职”则需要让出CPU给其他员工(Task)使用 */
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    }

#endif /* INCLUDE_vTaskDelete */

prvDeleteTCB

🧔 老李:
回收 员工(Task)工牌(TCB) 等资源。

关键步骤

  1. 调用平台相关的TCB清理函数
  2. 释放TCB_t 和 栈内存

参数:

TCB_t *pxTCB: 需要删除的Task的TCB

#if ( INCLUDE_vTaskDelete == 1 )

    static void prvDeleteTCB( TCB_t *pxTCB )
    {
        /* 清理相关的宏或函数,其具体实现依赖于目标硬件平台port层 */
        portCLEAN_UP_TCB( pxTCB );

        #if ( configUSE_NEWLIB_REENTRANT == 1 ) /* NewLib相关,暂不考虑 */
        {
            _reclaim_reent( &( pxTCB->xNewLib_reent ) );
        }
        #endif /* configUSE_NEWLIB_REENTRANT */

        /* 允许动态分配内存 并且 不允许静态分配内存 并且 没有启动MPU。则说明当前创建的Task是动分配的内存 */
        #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) )
        {
            /* 释放栈内存,必须先释放堆内存,如果先释放pxTCB,则pxTCB->pxStack为非法地址 */
            vPortFree( pxTCB->pxStack );
            /* 释放TCB内存 */
            vPortFree( pxTCB ); 
        }
        #elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /* 既 允许动态分配内存 又 允许静态分配内存,则需要判断调用prvDeleteTCB的是xTaskCreate 还是 xTaskCreateStatic */
        {
            
            if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB )
            {
                /* 当前Task是在xTaskCreate中创建的,可以先free pxStack 然后 free pxTCB,此处要注意free顺序防止发生访问非法地址 */
                /* Both the stack and TCB were allocated dynamically, so both
                must be freed. */
                vPortFree( pxTCB->pxStack );
                vPortFree( pxTCB );
            }
            else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY )
            {
                /* 当栈内存是静态分配的时候,只释放TCB_t */
                vPortFree( pxTCB );
            }
            else
            {
                /* 栈内存和TCB都是静态创建的,这时候configASSERT一下ucStaticallyAllocated */
                configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB );
                mtCOVERAGE_TEST_MARKER();
            }
        }
        #endif /* configSUPPORT_DYNAMIC_ALLOCATION */
    }

prvResetNextTaskUnblockTime

🧔 老李:
计算最近的一个员工休假回来的时间

关键步骤

  1. 计算下一个Delay任务解除阻塞的时间

参数:

void: 无参数

    static void prvResetNextTaskUnblockTime( void )
    {
    TCB_t *pxTCB;

        if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
        {
            /* 当DelayTaskList为空时,意味着没有排队等待执行的员工(Task),则这个时间是无限大,也就是portMAX_DELAY */
            xNextTaskUnblockTime = portMAX_DELAY;
        }
        else
        {
            /* 如果不为空,则获取DelayTaskList头部的Task,由于TCB->xStateListItem在插入pxDelayedTaskList时是按照xStateListItem(xTimeToWake需要Delay)的升序有序插入的,所以头部的Task就是最短Block时间 */
            ( pxTCB ) = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
            /* 获取xStateListItem也就是xTimeToWake */
            xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) );
        }
    }