【FreeRTOS】任务管理与调度

news2025/2/25 18:02:24

文章目录

  • 调度:
  • 总结


调度:

  1. 相同优先级的任务轮流运行
  2. 最高优先级的任务先运行

可以得出结论如下:

  • a 高优先级的任务在运行,未执行完,更低优先级的任务无法运行
  • b 一旦高优先级任务就绪,它会马上运行(假设厨房着火了,会马上去灭火)
  • c 如果最高优先级的任务有多个,他们轮流运行

他们都是使用链表进行管理

打开CubeMX,最高优先级56
在这里插入图片描述
在这里插入图片描述

56个List,

RadeyList[55] —>优先级为55的,处于就绪态的任务
RadeyList[54] —>优先级为54的,处于就绪态的任务
……
RadeyList[N] —>优先级为N的,处于就绪态的任务
……
RadeyList[0]

我们一开始创建了好几个任务,优先级默认都是osPriorityNormal=24

  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes); // 默认任务

  /* 创建任务:光 */ 
  // 创建一个静态分配内存的任务
  xLightTaskHandle = xTaskCreateStatic(
            Led_Test,           //LED测试函数,PC13以500ms间隔亮灭一次
            "LightTask",        //光任务
            128,                //栈大小,这里提供了栈的大小(长度)
            NULL,               //无传入的参数
            osPriorityNormal,   //优先级默认
            g_pucStackOfLightTask,  // 静态分配的栈,一个buffer,这里只提供了首地址,长度就是栈的大小,最开始栈的类型不对,栈的类型uint32_t
            &g_TCBofLightTask       // 取址TCB
  );
  


  /* 创建任务:色 */ 
  xColorTaskHandle = xTaskCreateStatic(
            ColorLED_Test,           //LED测试函数,PC13以500ms间隔亮灭一次
            "ColorTask",        //光任务
            128,                //栈大小,这里提供了栈的大小(长度)
            NULL,               //无传入的参数
            osPriorityNormal,   //优先级默认
            g_pucStackOfColorTask,  // 静态分配的栈,一个buffer,这里只提供了首地址,长度就是栈的大小
            &g_TCBofColorTask       // 取址TCB
  );

执行完上面的程序之后,会创建三个任务, 放在 RadeyList[24] 这个链表里面~其他链表都是空的

这里是怎么调度的呢?

启动调度器后
执行

osStatus_t osKernelStart (void) {
  osStatus_t stat;

  if (IS_IRQ()) {
    stat = osErrorISR;
  }
  else {
    if (KernelState == osKernelReady) {
      KernelState = osKernelRunning;
      vTaskStartScheduler();
      stat = osOK;
    } else {
      stat = osError;
    }
  }
  return (stat);
}

执行完之后,这里就创建了一个空闲的任务

在这里插入图片描述

void vTaskStartScheduler( void )
{
BaseType_t xReturn;

	/* Add the idle task at the lowest priority. */
	#if( configSUPPORT_STATIC_ALLOCATION == 1 )
	{
		StaticTask_t *pxIdleTaskTCBBuffer = NULL;
		StackType_t *pxIdleTaskStackBuffer = NULL;
		uint32_t ulIdleTaskStackSize;

		/* The Idle task is created using user provided RAM - obtain the
		address of the RAM then create the idle task. */
		vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
		xIdleTaskHandle = xTaskCreateStatic(	prvIdleTask,
												configIDLE_TASK_NAME,
												ulIdleTaskStackSize,
												( void * ) NULL, /*lint !e961.  The cast is not redundant for all compilers. */
												( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
												pxIdleTaskStackBuffer,
												pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */

		if( xIdleTaskHandle != NULL )
		{
			xReturn = pdPASS;
		}
		else
		{
			xReturn = pdFAIL;
		}
	}
	#else
	{
		/* The Idle task is being created using dynamically allocated RAM. */
		xReturn = xTaskCreate(	prvIdleTask,
								configIDLE_TASK_NAME,
								configMINIMAL_STACK_SIZE,
								( void * ) NULL,
								( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
								&xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
	}
	#endif /* configSUPPORT_STATIC_ALLOCATION */

	#if ( configUSE_TIMERS == 1 )
	{
		if( xReturn == pdPASS )
		{
			xReturn = xTimerCreateTimerTask();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_TIMERS */

	if( xReturn == pdPASS )
	{
		/* freertos_tasks_c_additions_init() should only be called if the user
		definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is
		the only macro called by the function. */
		#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
		{
			freertos_tasks_c_additions_init();
		}
		#endif

		/* Interrupts are turned off here, to ensure a tick does not occur
		before or during the call to xPortStartScheduler().  The stacks of
		the created tasks contain a status word with interrupts switched on
		so interrupts will automatically get re-enabled when the first task
		starts to run. */
		portDISABLE_INTERRUPTS();

		#if ( configUSE_NEWLIB_REENTRANT == 1 )
		{
			/* Switch Newlib's _impure_ptr variable to point to the _reent
			structure specific to the task that will run first. */
			_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
		}
		#endif /* configUSE_NEWLIB_REENTRANT */

		xNextTaskUnblockTime = portMAX_DELAY;
		xSchedulerRunning = pdTRUE;
		xTickCount = ( TickType_t ) 0U;

		/* If configGENERATE_RUN_TIME_STATS is defined then the following
		macro must be defined to configure the timer/counter used to generate
		the run time counter time base.   NOTE:  If configGENERATE_RUN_TIME_STATS
		is set to 0 and the following line fails to build then ensure you do not
		have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your
		FreeRTOSConfig.h file. */
		portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

		/* Setting up the timer tick is hardware specific and thus in the
		portable interface. */
		if( xPortStartScheduler() != pdFALSE )
		{
			/* Should not reach here as if the scheduler is running the
			function will not return. */
		}
		else
		{
			/* Should only reach here if a task calls xTaskEndScheduler(). */
		}
	}
	else
	{
		/* This line will only be reached if the kernel could not be started,
		because there was not enough FreeRTOS heap to create the idle task
		or the timer task. */
		configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
	}

	/* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
	meaning xIdleTaskHandle is not used anywhere else. */
	( void ) xIdleTaskHandle;
}

函数名:prvIdleTask 空闲任务

现在的链表

在这里插入图片描述

在这里插入图片描述
这句话是把这个新的任务添加进ReadyList,添加进就绪列表,根据优先级找到链表

还有一个全局变量当前TCB

在这里插入图片描述
最开始只有StartDefaultTask这一个任务,那么全局指针pxCurrentTCB就指向这个任务

在这里插入图片描述

当我们创建led的任务时,指针指向Led_Test
在这里插入图片描述

当创建ColorLED_Test时候,指针就指向了ColorLED_Test
在这里插入图片描述

启动调度器的时候,创建了一个空闲任务,但是优先级是0,优先级比较低,pxCurrentTCB保留,还是指向ColorLED_Test

当我们真正启动调度器的时候,就从ColorLED_Test任务开始,先运行最后创建的任务

  • =这就是06_create_task_use_params里,后面创建的task3最先开始运行的原因!!!=

在系统里,会初始化一个Tick,Tick中断里面有计数累加,作为时钟基准,并且会调度

cubemx定义了一个时钟,频率是1000,每1ms产生一次中断
在这里插入图片描述

Tick中断:

  1. 计数累加,作为时钟基准
  2. 并且会发起调度

调度:从上到下遍历ReadyList,找到24的时候,非空,然后指向下一个任务

  • 遍历ReadyList,找到第一个非空的链表,把pxCurrentTCB指向下一个任务,然后启动

在这里插入图片描述

运行1ms之后,计数值又累计,发起下一次调度,再次遍历这些链表,从上面找到下面,找到第24个不空,把pxCurrentTCB指向下一个任务,然后启动

在这里插入图片描述

下一次调度

在这里插入图片描述

上面就是轮流运行这些相同优先级的就绪态任务的,运行依赖于tick中断,依赖于调度机制

在默认任务里,读取遥控器的键值,如果是播放按键,会创建一个播放音乐的任务,优先级是25,如图所示:

在这里插入图片描述

这个任务马上处于就绪态,并且是最高优先级,所以会马上运行这个任务

在这里插入图片描述

当执行到vTaskDelay的时候,现在是Blocked状态,已经不是Ready状态了,现在就会从25优先级的链表中删除,移动到某一个delay的链表里,有DelayedTaskList1和DelayedTaskList2(这里是防止定时器溢出,才有两个delay链表)

在这里插入图片描述

假设存放到DelayedTaskList1里了,下一次Tick中断的时候,现在又要发起一次调度,重新遍历……
取出下一个任务来运行(上次运行的是任务1,下一个任务就是任务2)
在这里插入图片描述

下一次中断,在调度如下图所示:

在这里插入图片描述

在Tick中断里发起一个比较复杂的调度,同时也判断一下delay链表你的时间有没有到,如果时间到了,就把它放入就绪链表

  1. 计数值 ++
  2. 判断DelayedTaskList里的任务是否可恢复,
  3. 发起调度

假设5号任务是2个Tick,5号任务的Tick已经到了,就需要把这个5号任务从delay链表里移动到第25个优先级的链表中去

然后发起一个调度,再遍历所有链表,发现25非空,就执行5号任务~

在5号任务里又使用了delay,又把这个任务放到DelayedTaskList里

在这里插入图片描述

然后遍历整个链表,发现第24项非空,又执行下一个任务(任务1)

在这里插入图片描述

假设在任务1里又调用了这个函数(按下了播放键,暂停的功能)
在这里插入图片描述

这个任务被暂停了,是不可能通过Tick中断唤醒的,只能Resume(从链表中移除,重新叨叨ReadyList里)

上面全是链表的操作


总结

  • 在Tick中断里计数值++
  • 判断DelaydeTaskList里的任务是否可以被恢复,如果时间到了,就移动到就绪态链表中
  • 发起调度,在找到非空链表中,找到下一个任务运行~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1854355.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI大模型企业应用实战(14)-langchain的Embedding

1 安装依赖 ! pip install --upgrade langchain ! pip install --upgrade openai0.27.8 ! pip install -U langchain-openai ! pip show openai ! pip show langchain ! pip show langchain-openai 2 Embed_documents # 1. 导入所需的库 from langchain_openai import Open…

阿里云服务器618没想到这么便宜,买早了!

2年前,我买了个服务器,租用服务器(ECS5)和网络宽带(1M),可以说是非常非常低的配置了。 当时5年的折扣力度最大,但是打完折后,价格依然要近3000多元。 最近看到阿里云618活…

C++风流和MATLAB | Python | CUDA 库埃特流泊肃叶流薄膜流体

🎯要点 🎯无滑移速度边界条件:🖊反弹法计算库埃特流、泊肃叶流 | 🖊湿节点法计算库埃特流、泊肃叶流 | 🎯力模型:🖊反弹法和不同的格子玻尔兹曼体力模型计算泊肃叶流 | &#x1f58…

winmail添加gmail和QQ邮箱(现已更新为outlook mail)

想在windows自带的邮件桌面应用里,不仅能访问outlook邮件,也能访问gmail邮件和QQ邮件的方法。 参考文章: Windows 10 的邮件怎么添加并同步 Gmail?​www.zhihu.com/question/53079836/answer/147669935?utm_psn178781450843941…

预训练是什么?

预训练是什么? 图像领域的预训练 在介绍图像领域的预训练之前,我们首先介绍下卷积神经网络(CNN),CNN 一般用于图片分类任务,并且CNN 由多个层级结构组成,不同层学到的图像特征也不同&#xff…

【LLM之KG】CoK论文阅读笔记

研究背景 大规模语言模型(LLMs)在许多自然语言处理(NLP)任务中取得了显著进展,特别是在零样本/少样本学习(In-Context Learning, ICL)方面。ICL不需要更新模型参数,只需利用几个标注…

谁说串口通信波特率越高越好?

在电子世界里,串口通信就像是电子设备之间的“悄悄话”,它们通过串行数据传输来交换信息。但你知道吗?串口通信的波特率并不是越高越好,这事儿得好好聊聊。 1.什么是串口通信? 串口通信,就像它的名字一样&a…

【转型指南】从软件测试到技术多面手

★ 导言 小艺是一位毕业于985的计算机硕士,工作多年,现在某大厂从事软件测试方面的管理工作。目前在工作中游刃有余,但面对技术的飞速变化和职业发展的不确定性,还是难免焦虑,正在积极思考如何进一步提升自己&#xff…

谈谈面试常考题:懒加载,防抖,节流(方法实现详解)

前言 最近在学习中确实收获了挺多东西,其中我觉得有必要拿来进行分享一下的就是懒加载了,还有相关的防抖和节流。因为在浏览器中这些都是属于很常见的性能优化,面试也是常考题。话不多说,速度发车。 什么是懒加载?懒…

关于Pytorch转换为MindSpore的一点建议

一、事先准备 必须要对Mindspore有一些了解,因为这个框架确实有些和其它流程不一样的地方,比如算子计算、训练过程中的自动微分,所以这两个课程要好好过一遍,官网介绍文档最好也要过一遍 1、零基础Mindspore:https://…

pytest测试框架flaky插件重试失败用例

Pytest提供了丰富的插件来扩展其功能,本章介绍下插件flaky ,用于在测试用例失败时自动重新运行这些测试用例。与前面文章介绍的插件pytest-rerunfailures功能有些类似,但是功能上不如pytest-rerunfailures插件丰富。 flaky官方并没有明确pyt…

微软搁置水下数据中心项目——项目纳蒂克相比陆地服务器故障更少

“我的团队努力了,并且成功了,”COI负责人诺埃尔沃尔什说。 微软已悄然终止了始于2013年的水下数据中心(UDC)项目“纳蒂克”。该公司向DatacenterDynamics确认了这一消息,微软云运营与创新部门负责人诺埃尔沃尔什表示…

多路h265监控录放开发-(12)完成全部开始录制和全部停止录制代码

xviewer.h 新增 public: void StartRecord();//126 开始全部摄像头录制 void StopRecord();//126 停止全部摄像头录制 xviewer.cpp 新增 //视频录制 static vector<XCameraRecord*> records;//126void XViewer::StartRecord() //开始全部摄像头录制 126 {StopRecord…

vuex的深入学习[基于vuex3]----篇(二)

store对象的创建 store的传递图 创建语句索引 创建vuex的语句为new Vuex.Store({…})Vuex的入口文件是index.js,store是index.js导出的store类store类是store.js文件中定义的。 Store的构造函数constructor 判断vuex是否被注入&#xff0c;就是将vue挂载在window对象上&am…

[技术笔记] 元器件采购之Flash的国内、外厂商Top5

国外Top5 1、Micron&#xff08;镁光&#xff09;半导体 2、Toshiba&#xff08;东芝&#xff09; 3、Hynix&#xff08;海力士&#xff09; 4、Samsung&#xff08;三星&#xff09; 5、Intel&#xff08;因特尔&#xff09; 6、SanDisk&#xff08;闪迪&#xff09; 7…

瑞_MongoDB_MongoDB副本集

文章目录 1 MongoDB副本集-Replica Sets1.1 简介1.2 副本集的三个角色1.3 副本集架构目标1.4 副本集的创建1.4.1 创建主节点1.4.2 创建副本节点1.4.3 创建仲裁节点1.4.4 初始化配置副本集和主节点1.4.5 查看副本集的配置内容 rs.conf()1.4.6 查看副本集状态1.4.7 添加副本从节点…

1.4 Kettle 数据同步工具详细教程

工具介绍 一、概述 Kettle&#xff0c;又名 Pentaho Data Integration&#xff08;PDI&#xff09;&#xff0c;是一个开源的数据集成工具&#xff0c;最初由 Pentaho 公司开发。它能够从多种数据源提取、转换并加载&#xff08;ETL&#xff09;数据&#xff0c;适用于数据仓…

2023-2024 学年第二学期小学数学六年级期末质量检测模拟(制作:王胤皓)(90分钟)

word效果预览&#xff1a; 一、我会填 1. 1.\hspace{0.5em} 1. 一个多位数&#xff0c;亿位上是次小的素数&#xff0c;千位上是最小的质数的立方&#xff0c;十万位是 10 10 10 和 15 15 15 的最大公约数&#xff0c;万位是最小的合数&#xff0c;十位上的数既不是质数也…

(Amazing!) 通过 vfox 在 Windows 上安装管理多个 Erlang/OTP 和 Elixir 的版本

大概一个多月前, 我写了篇关于如何使用跨平台版本管理工具 vfox 在 Linux 系统下安装管理多个 Erlang/OTP 版本的文章 -> 通过 vfox 安装管理多版本 Erlang 和 Elixir. 文章使用的示范操作系统是 Ubuntu 20.04 Linux 操作系统. 最近 vfox-erlang 和 vfox-elixir 插件的最新…