【FreeRTOS】任务管理与调度

文章目录

  • 调度:
  • 总结


调度:

  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.mfbz.cn/a/736839.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

笔记本更换固态,保留数据,无需重装系统和软件

一、问题描述: 原有一块128GB的固态硬盘作为c盘使用,由于工作学习需要,经常跑虚拟机,现在需要升级容量。 二、解决思路: 硬件 购买一款大容量的固态硬盘 不同的容量有不同的价格,这个根据预算和实际需要来…

Android,RPC原理,C语言实现Binder跨进程通信Demo

RPC原理图 Binder C语言层的Demo演示 新建目录 把两个文件拷贝到我们的Demo下面 1.binder_server.c #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <linux/types.h> #include <stdbool.h> #include <string.h> #…

odoo的采购询价单,默认情况下显示‘draft‘,‘sent‘,‘purchase‘,请问什么情况下才会显示‘to approve‘?

odoo的采购询价单&#xff0c;默认情况下显示’draft’,‘sent’,‘purchase’&#xff0c;请问什么情况下才会显示’to approve’? 见下图&#xff1a; 这与操作人员的角色是相关的&#xff1a; 当操作人员是群组 “采购 / 用户”时&#xff0c;点击“confirm order/确认订…

越复杂的CoT越有效吗?Complexity-Based Prompting for Multi-step Reasoning

Complexity-Based Prompting for Multi-step Reasoning 论文&#xff1a;https://openreview.net/pdf?idyf1icZHC-l9 Github&#xff1a;https://github.com/FranxYao/chain-of-thought-hub 发表位置&#xff1a;ICLR 2023 Complexity-Based Prompting for Multi-step Reason…

【C语言】算法:二分查找

当我们想在一个有序的序列里面查找一个数字的时候&#xff0c;通常会想到使用循环遍历&#xff0c;也就是下面这种方法&#xff1a; 比如我们想在下面的数组里面找到7&#xff1a; int main() {int num 7;int arr[10] { 1,2,3,4,5,6,7,8,9,10 };for (int i 0; i < size…

5.树莓派4b+ubuntu18.04(ros版本melodic)+arduino mega自制两轮差速小车,实现建图导航功能

这一节介绍雷达的使用&#xff0c;我们使用的雷达型号是ydlidar x3 1.进入工作空间 cd catkin_ws/src2.下载官方提供的SDK文件 git clone https://github.com/YDLIDAR/YDLidar-SDK.git3.安装cmake sudo apt install cmake pkg-config4.编译和安装 进入YDLidar-SDK文件夹后如…

Linux动态网站架构(部署开发php代码)

动态网站架构&#xff08;部署开发php代码&#xff09; 测试能否直接部署nginx需要什么服务&#xff0c;及原理准备并进行开发测试部署代码 概述 静态网站&#xff1a;图片仅仅包含&#xff1a;html&#xff0c;css样式js脚本&#xff0c;图片及视频&#xff1b;nginx直接处…

DHCP原理1-单个局域网出现多个DHCP服务器会发生什么

1. 背景 DHCP全称是Dynamic Host Configuration Protocol。其协议标准是RFC1541&#xff08;已被RFC2131取代&#xff09;&#xff0c;主要实现服务器向客户端动态分配IP地址&#xff08;如IP地址、子网掩码、网关、DNS&#xff09;和配置信息。其系统架构是标准的C/S架构。RFC…

如何写出高效的代码?

1. 优化你的程序&#xff0c;拒绝创建不必要的对象 如果你的变量&#xff0c;后面的逻辑判断&#xff0c;一定会被赋值&#xff1b;或者说&#xff0c;只是一个字符串变量&#xff0c;直接初始化字符串常量就可以了&#xff0c;没有必要愣是要new String(). 反例&#xff1a;…

Redis 的安装与部署

本文为Redis的Linux版单机部署。 上传 redis-3.2.8 源码到 /opt/software/ 解压到 /opt/module/ [huweihadoop101 software]$ tar -zxvf redis-3.2.8.tar.gz -C /opt/module/安装依赖 [huweihadoop101 software]$ sudo yum -y install gcc-c tclRedis是C语言编写的 编译安装…

(南京观海微电子)——DC-DC和LDO的原理及应用区别

LDO: 低压差线性稳压器&#xff0c;故名思意为线性的稳压器&#xff0c;仅能使用在降压应用中&#xff0c;也就是输出电压必需小于输入电压。 优点&#xff1a;稳定性好&#xff0c;负载响应快&#xff0c;输出纹波小。 缺点&#xff1a; 效率低&#xff0c;输入输出的电压…

Linux使用——查看发行版本、内核、shell类型等基本命令

先做快照 虚拟机中编辑网络 关机 普通账户和管理员账户 互相对照 localhost相当于IP 参数: 短格式:以减号(-)开头&#xff0c;参数字母 长格式:以2个减号(--)后跟上完整的参数单词 当前发行版本 [rootserver ~]# cat /etc/redhat-release Red Hat Enterprise Linux release 9.…

.hmallox勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复

导言&#xff1a; 在当今数字化时代&#xff0c;勒索病毒已经成为网络安全的一大威胁&#xff0c;其中包括了最近出现的.hmallox勒索病毒。这类恶意软件不仅能够对计算机系统进行加密&#xff0c;还会要求用户支付赎金以换取解密密钥&#xff0c;给个人用户和企业带来了严重的…

Java面试题:数据库索引

数据库索引 索引:index 帮助mysql高效获取数据的数据结构,在数据之外,数据库系统还维护着满足特定查找算法的数据结构(B树),这些数据结构以某种方式引用(指向数据),这样就可以在数据结构上实现高级查找算法 将数据以树的方式进行存储,提高查找效率 索引的底层结构 使用B树 …

React AntDesign Layout组件布局刷新页面错乱闪动

大家最近在使用React AntDesign Layout组件布局后刷新页面时&#xff0c;页面布局错乱闪动 经过组件属性的研究才发现&#xff0c;设置 hasSider 为 true 就能解决上面的问题&#xff0c;耽搁了半天的时间&#xff0c;接着踩坑接着加油&#xff01;&#xff01;&#xff01; …

XTDrone-多无人机精准降落-配置教程

1 编译AprilTag_ROS AprilTag是一个视觉基准系统&#xff0c;可用于机器人&#xff0c;增强现实和相机校准等。 根据AprilTag可以可靠地计算标签相对于相机的3D位置&#xff0c;方向和ID号。这里我们使用AprilTag的ROS库来实现位姿估计与ID号计算。 编译命令如下&#xff1a; …

Linux 7种 进程间通信方式

传统进程间通信 通过文件实现进程间通信 必须人为保证先后顺序 A--->硬盘---> B&#xff08;B不知道A什么时候把内容传到硬盘中&#xff09; 1.无名管道 2.有名管道 3.信号 IPC进程间通信 4.消息队列 5.共享内存 6.信号灯集 7.socket通信 一、无名管道&a…

基于I2C协议的AHT20温湿度传感器的数据采集

一、I2C总线通信协议 软件I2C 软件I2C&#xff0c;也称为模拟I2C或bit-bang I2C&#xff0c;是一种通过微控制器的通用输入输出&#xff08;GPIO&#xff09;引脚来模拟I2C总线通信的方式。它不依赖于专门的硬件I2C接口&#xff0c;而是通过编程控制GPIO引脚的电平状态来实现I…

我在高职教STM32——LCD液晶显示(3)

大家好&#xff0c;我是老耿&#xff0c;高职青椒一枚&#xff0c;一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次&#xff0c;同行应该都懂的&#xff0c;老师在课堂上教学几乎是没什么成就感的。正因如此&#xff0c;才有了借助 CSDN 平台寻求认同感和成就…

前端开发流程与技术选型

目录 一、简介 二、前端职责 三、开发步骤 四、技术选型 五、页面展示 一、简介 做一个网站时&#xff0c;能看到的一切都是前端程序员的工作&#xff0c;负责网页或者app的结构、样式、用户操作网站时的事件逻辑&#xff08;比如点击一个按钮&#xff09;。 二、前端职…