任务API

任务控制块

typedef struct tskTaskControlBlock      
{
    volatile StackType_t * 	 pxTopOfStack;				//任务栈顶,必须为TCB第一成员,栈顶存放当前任务的所有内核寄存器值,任务切换时,将栈顶的内核寄存器加载到内核中 即恢复执行,上下文切换时会更新栈顶指针     
    
    #if ( portUSING_MPU_WRAPPERS == 1 )
		xMPU_SETTINGS	xMPUSettings;		//MPU保护设置
	#endif
    
    ListItem_t 				xStateListItem;             //任务状态列表,当前任务处于什么状态
    ListItem_t 				xEventListItem;             //任务事件列表,当前任务在等待什么事件
    UBaseType_t 			uxPriority;                 //任务优先级 数值越大 优先级越大    
    StackType_t * 			pxStack;                    //任务栈起始地址  从高地址往低地址生长
    char 				   pcTaskName[ configMAX_TASK_NAME_LEN ]; //任务名称
    
 	#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )	
		StackType_t		*pxEndOfStack;		// 堆栈结束地址 用于堆栈溢出检测
	#endif

	#if ( portCRITICAL_NESTING_IN_TCB == 1 )
		UBaseType_t		uxCriticalNesting;	// 临界区 嵌套次数
	#endif

	#if ( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t		uxTCBNumber;		// TCB创建序号	用于区分 删除后重新创建的同名任务
		UBaseType_t		uxTaskNumber;		// 任务编号
	#endif

	#if ( configUSE_MUTEXES == 1 )
		UBaseType_t		uxBasePriority;		// 基础优先级
		UBaseType_t		uxMutexesHeld;		//持有的互斥数量
	#endif

	#if ( configUSE_APPLICATION_TASK_TAG == 1 )
		TaskHookFunction_t pxTaskTag;		//任务标签 
	#endif

	#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
		void			*pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];	// 线程本地存储 任务私有数据指针数组
	#endif

	#if( configGENERATE_RUN_TIME_STATS == 1 )
		uint32_t		ulRunTimeCounter;	// 运行时间统计
	#endif

	#if ( configUSE_NEWLIB_REENTRANT == 1 )
		struct	_reent xNewLib_reent;		//NewLib 可重入结构
	#endif

	#if( configUSE_TASK_NOTIFICATIONS == 1 )
		volatile uint32_t ulNotifiedValue;	//任务通知值
		volatile uint8_t ucNotifyState;		//任务通知状态
	#endif


	#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 
		uint8_t	ucStaticallyAllocated; 		//静态分配标记 标识任务是否时静态分配的
	#endif

	#if( INCLUDE_xTaskAbortDelay == 1 )
		uint8_t ucDelayAborted;				// 延迟是否被中止  标记任务的延迟是否被xTaskAbortDelay()中止
	#endif

	#if( configUSE_POSIX_ERRNO == 1 )
		int iTaskErrno;						// 为每个任务提供独立的 errno 变量  
	#endif
} tskTCB;

任务创建

动态创建

  • 任务控制块、任务栈空间由系统从管理的堆中分配

  • 创建完成后 立即进入就绪态

  • 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	   //任务句柄指针,就是任务控制块
                          )
    //返回值   pdPASS-成功,其他-失败
    

动态创建函数解析

  • 申请堆栈内存,返回首地址

  • 申请任务控制块内存,返回首地址

  • 把申请到的堆栈地址,赋值给控制块的 pxStack

  • 调用prvInitialiseNewTask 初始化任务控制块中的成员

    • 把整个任务堆栈初始化为0xA5,方便计算最小剩余空间,不为A5则被使用过
    • 获取栈顶地址,向下依次 8字节对齐
    • 初始化状态列表项、事件列表项
    • pxPortInitialiseStack 函数 将xPSR、PC、LR 等所有内核寄存器 存入栈顶,当任务切换时 将内核中的寄存器值保存到任务栈顶,恢复任务时 读取栈顶的寄存器值 再顺序执行
    • 任务句柄 等于 任务TCB
  • 调用prvAddNewTaskToReadyList 添加新创建任务到就绪列表中

    • 任务数量计数器加一 uxCurrentNumberOfTasks++

    • 判断新创建的任务是否为第一个任务 (pxCurrentTCB指向当前优先级最高的任务 即正在执行的任务)

      • 如果创建的是第一个任务,初始化各个任务列表 prvInitaliseTaskLists()
      • 如果创建的不是第一个任务 并且调度器还未开始启动,比较新任务与正在执行的任务优先级大小,新任务优先级大的话,将pxCurrentTCB 重新指向新的控制块
    • 将新的TCB添加到就绪列表中,使用 prvAddTackToReadyList 函数

      • 32个优先级,每一位一个列表,当前优先级有任务就绪则 这一位置1
    • 如果调度器已经开始运行,并且新任务的优先级更大的话 进行一次任务切换

静态创建

  • 任务控制块、任务栈空间由用户自己分配

  • TaskHandle_t xTaskCreateStatic( TaskFunction_t 			pxTaskCode,		//任务函数指针
                                    const char * const 	     pcName,  	//任务名称,最大长度
                                    const uint32_t 			ulStackDepth,//任务堆栈大小 字 为单位
                                    void * const 		    pvParameters,//参数
                                    UBaseType_t 		    uxPriority, //任务优先级
                                    StackType_t * const 	puxStackBuffer,//任务堆栈,一般为数组
                                    StaticTask_t * const 	pxTaskBuffer //任务控制 块指针
                                  )
     //返回值   NULL-失败,其他-任务句柄
    

静态创建函数解析

  • 打开 configSUPPORT_STATIC_ALLOCATION 宏
  • 定义空闲任务、定时器任务的任务堆栈及TCB
  • 实现两个空间申请接口函数
    • vApplicationGetIdleTaskMemory()
    • vApplicationGetTimerTaskMemory()
  • 定义任务创建函数入口参数
  • 编写任务函数

任务删除

void vTaskDelete( TaskHandle_t xTaskToDelete ); //参数为 任务句柄
  • 空闲任务会负责释放被删除任务中由系统分配的内存
  • 由用户分配的空间删除任务前需手动释放 否则会内存泄漏
  • 使用前需要开启配置文件中宏定义

删除函数解析

  • 获取要删除任务的TCB
    • 通过任务句柄 判断要删除哪个任务,NULL删除自身
  • 将被删除任务 移除所在列表
    • 将该任务在 所在所有列表中移除
  • 判断要删除的任务
    • 删除自身,先添加到等待删除列表 内存释放在空任务执行
    • 删除其他 当前任务数量减一,更新下一任务的阻塞超时时间,以防被删除的任务就是下一阻塞超时的任务
  • 删除的任务为其他任务则直接释放内存prvDeleteTCB()
  • 调度器正在运行且删除任务自身,则需要进行一次任务切换

任务挂起

void vTaskSuspend( TaskHandle_t xTaskToSuspend );	//任务句柄
  • 使用前 INCLUDE_vTaskSuspend 宏置1

挂起函数解析

  • 根据任务句柄获取TCB,如果为NULL 则挂起任务本身
  • 将要挂起的任务从相应列表和事件中移除
  • 将该任务的任务状态列表项插入到挂起状态列表末尾
  • 判断任务调度器是否运行,若在运行,更新下一次阻塞时间,防止被挂起任务为下一次阻塞超时任务
  • 如果挂起的是任务本身
    • 调度器在运行,强制进行一次任务切换
    • 调度器没有运行,判断挂起任务数是否等于任务总数
      • 是,表示所有任务被挂起,则 pxCurrentTCB 赋值为NULL
      • 否,通过函数vTaskSwitchContext 寻找下一个最高优先级任务

任务恢复

void vTaskResume( TaskHandle_t xTaskToResume );//任务句柄
  • 使用前 INCLUDE_vTaskSuspend 宏置1
  • 恢复后就 进入就绪态

恢复函数解析

  • 恢复任务不能是正在运行的任务且不能为NULL
  • 判断任务是否被挂起
    • 是,将任务从挂起列表中移除,并添加进就绪列表中
  • 判断恢复的任务的优先级是否大于当前正在执行的任务,是则执行任务切换
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume );//中断中恢复 
//返回值 pdRTUE-任务恢复后需要进行任务切换,pdFALSE-任务恢复后不需要进行任务切换
  • 在中断中使用,需要该中断优先级不能高于FreeRTOS 所管理的中断的最高优先级
  • 使用前 INCLUDE_vTaskSuspend 、INCLUDE_xTaskResumeFromISR宏置1

中断中恢复函数解析

  • 函数portASSERT_IF_INTERRUPT_PRIORITY_INVALID(),用于检测调用FREERTOS的API函数的中断优先级是否在管理范围内 及 是否全部设置为抢占式优先级位
  • 关闭freeRTOS可管理中断,防止被其他的中断打断,并 返回关闭前 basepri寄存器 的值
  • 判断是否有挂起任务
    • 有,则检查调度器是否被挂起
      • 未挂起
        • 判断恢复的这个任务优先级 是否大于正在执行的任务
          • 是 则将xYieldRequired标记位pdTRUE,表示需要进行一次任务切换
        • 将被恢复的任务从挂起列表移除
        • 插入就绪列表
      • 被挂起
        • 恢复的任务插入等待就绪列表xPendingReadyList 直到调度器被恢复再进行任务的处理
    • 无,则不操作
  • 将前面保存的basepri的值 恢复
  • 返回xYieldRequired 的值,用于决定是否需要进行任务切换

任务状态查询

uxTaskPriorityGet()

  • 获取任务优先级

vTaskPrioritySet()

  • 设置任务优先级

uxTaskGetSystemState()

  • 获取所有任务的状态信息

vTaskGetInfo()

  • 获取单个任务的状态信息

xTaskGetApplicationTaskTag()

  • 获取任务 Tag

xTaskGetCurrentTaskHandle()

  • 获取当前任务的任务句柄

xTaskGetHandle()

  • 获取指定任务的任务句柄

xTaskGetIdleTaskHandle()

  • 获取空闲任务的任务句柄

uxTaskGetStackHighWaterMark()

  • 获取任务的任务栈历史剩余最小值

eTaskGetState()

  • 获取任务状态

pcTaskGetName

  • 获取任务名

xTaskGetTickCount()

  • 获取系统时钟节拍计数器的值

xTaskGetTickCountFromISR()

  • 中断中获取系统使用节拍计数器的值

xTaskGetSchedulerState()

  • 获取任务调度器状态

uxTaskGetNumberOfTasks()

  • 获取系统中任务的数量

vTaskList()

  • 以“表格”形式获取所有任务的信息

vTaskGetRunTimeStats()

  • 获取任务的运行时间等信息

vTaskSetApplicationTaskTag()

  • 设置任务 Tag

SetThreadLocalStoragePointer()

  • 设置任务的独有数据记录数组指针

GetThreadLocalStoragePointer()

  • 获取任务的独有数据记录数组指针

任务运行时间统计

vTaskGetRunTimeStats()

  • 用于获取指定任务的运行时间、状态等信息
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐