嵌入式FreeRTOS学习九,任务链表的构成,TICK时间中断和任务状态切换调度

2023-05-16

一. tskTaskControlBlock  函数结构体

在tskTaskControlBlock 任务控制块结构体中,其中有任务状态链表和事件链表两个链表成员,首先介绍任务状态链表这个结构,这个链表通常用于管理不同状态的任务;通常,操作系统任务有4种状态,就绪态ready,运行态running,阻塞态blocked和暂停态suspend,这些状态在任务状态链表里是怎么被管理的呢?

typedef struct tskTaskControlBlock             
    {
        // 这里栈顶指针必须位于TCB第一项是为了便于上下文切换操作,详见xPortPendSVHandler中任务切换的操作。
        volatile StackType_t    *pxTopOfStack;           
        // 表示任务状态,不同的状态会挂接在不同的状态链表下
        ListItem_t            xStateListItem;    
        // 事件链表项,会挂接到不同事件链表下
        ListItem_t            xEventListItem;        
        // 任务优先级,数值越大优先级越高
        UBaseType_t            uxPriority;            
        // 指向堆栈起始位置,这只是单纯的一个分配空间的地址,可以用来检测堆栈是否溢出
        StackType_t            *pxStack;            
        // 任务名
        char                pcTaskName[ configMAX_TASK_NAME_LEN ];
    } tskTCB;
    typedef tskTCB TCB_t;

怎么管理处于不同状态的任务?
例如:怎么取出要运行的任务?

  • 找到最高优先级的运行态、就绪态任务,运行它
  • 如果大家平级,轮流执行:排队,链表前面的先运行,运行1个tick后再去链表后面排队

=========================================================================

二.调用xTaskCreate函数后的执行过程

为了更好地探讨这个问题,我们得回归FreeRTOS源码中查看tack.c文件,分析创建一个任务的时候,不同状态的任务会被放入到哪个任务状态链表里面?任务状态链表是怎么管理不同状态的任务的?分析源码可以很清晰地看到处理思路!

  • 在task.c文件中,可以看到在调用xTaskCreate函数后,系统随后的工作为TCB结构体分配内存空间,将pxStack指针指向分配的内存空间的起始地址。

  • 首先对任务结构体TCB进行了判断,创建成功后进入条件中,对任务进行prvInitialiseNewTask初始化以后,将新的任务放入到prvAddNewTaskToReadyList( pxNewTCB )就绪链表之中,再调用其它函数。

  • 那么是怎么把任务放入就绪链表的呢?就绪链表的结构又是怎样的?它是怎么管理不同任务状态的任务呢?之前在创建任务函数中调用了prvAddNewTaskToReadyList( pxNewTCB )函数接口后;进入prvAddNewTaskToReadyList( TCB_t * pxNewTCB )这个函数中。

  • prvAddNewTaskToReadyList( TCB_t * pxNewTCB )这个函数中,这个函数实际上是调用了 prvAddTaskToReadyList( pxTCB ) 函数

  • 跳转到prvAddTaskToReadyList( pxTCB ) 函数,从这里可以看出,这里将任务插入到链表的尾部,pxReadyTasksLists是一个由多个链表数组构成的结构体,里面包含了很多不同优先级的链表结构,具体是插入哪一个链表,由任务的优先级uxPriority来决定。 

 =========================================================================

三.链表的组成结构

任务在就绪态时的切换

在FreeRTOS操作系统的源码中,可以看到有5种类型的链表,这里我们只用到三种类型,这三种分别为pxReadyTasksLists就绪链表,xDelayedTaskList1/xDelayedTaskList2阻塞链表, xPendingReadyList休眠链表;  其中延时链表和休眠链表这两类只有一个链表。就绪链表的种类则和优先级的大小有关,也可以由代码宏控制设定。

PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; 
PRIVILEGED_DATA static List_t xDelayedTaskList1;                         
PRIVILEGED_DATA static List_t xDelayedTaskList2;                        
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;             
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;      
PRIVILEGED_DATA static List_t xPendingReadyList;                       

上述的pxReadyTasksLists链表的有5个优先级,链表的结构为

//在task.c文件中
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
//在FreeRTOS.h文件中
#define configMAX_PRIORITIES		( 5 )

可以看出pxReadyTasksLists链表有5个优先级,因此该链表是由5个链表结构组成的,在例子中,我们创建了3个任务函数,这三个任务分别根据不同的优先级进入不同的任务就绪链表中。

int main( void )
{
	prvSetupHardware();	
	xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
	xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);
	xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);
	/* 启动调度器 */
	vTaskStartScheduler();
	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

FreeRTOS操作系统中,优先级为3的Task 3函数进入pxReadyTasksList[2]的链表中,优先级为0的Task 2函数进入pxReadyTasksList[0]的链表中,优先级为0的Task 1函数进入pxReadyTasksList[0]的链表中,任务在处于在同一状态下的情况下,系统会按照从大到小的顺序遍历这些链表,即优先级的顺序,链表中有任务则将任务拿出来执行。

=========================================================================四.任务的调度问题

FreeRTOS操作系统中,由TICK中断执行任务的调度,所谓TICK中断,就是每隔固定时间,系统会产生的定时器中断,可以由代码设定时间轴,每隔设定的固定时间产生一个TICK中断。FreeRTOS操作系统的间隔配置通常是1ms。产生中断后,就会去调用TICK中断函数。

对于FreeRTOS操作系统,TICK中断比较简单,每个任务只能执行一个TICK。因为Task 2最后插入链表,所以Task 1先执行,Task 1执行1个tick后,Task 1会中断,马上切换到Task 2执行。系统会保存当前任务Task 1的寄存器现场,然后执行新任务。Task 2执行一个TICK后,也是如此,保存Task 2的寄存器现场,然后寻找新的任务执行。

注意看下面的任务 vTask3的任务函数,里面加了vTaskDelay( xDelay5ms )延时函数,因为vTask3是最高优先级任务,如果不加延时,系统会一直执行vTask3,其它的任务完全不能抢占和执行,vTask1和vTask2根本没有机会执行,不会打印任何信息。

void vTask1( void *pvParameters )
{
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		flagIdleTaskrun = 0;
		flagTask1run = 1;
		flagTask2run = 0;
		flagTask3run = 0;
		
		/* 打印任务的信息 */
		printf("T1\r\n");				
	}
}

void vTask2( void *pvParameters )
{	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		flagIdleTaskrun = 0;
		flagTask1run = 0;
		flagTask2run = 1;
		flagTask3run = 0;
		
		/* 打印任务的信息 */
		printf("T2\r\n");				
	}
}
void vTask3( void *pvParameters )
{	
	const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );		
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		flagIdleTaskrun = 0;
		flagTask1run = 0;
		flagTask2run = 0;
		flagTask3run = 1;		
		/* 打印任务的信息 */
		printf("T3\r\n");				
		// 如果不休眠的话, 其他任务无法得到执行
		vTaskDelay( xDelay5ms );
	}
}

执行完vTaskDelay( xDelay5ms )这个函数后,vTask3这个任务就会从任务就绪状态改变为任务阻塞状态,也就是从就绪链表被移到阻塞链表。然后系统再从任务就绪链表里面寻找可以执行的任务,任务vTask1和vTask2有机会得到执行。vTaskDelay( xDelay5ms )实际上就是改变任务的状态。vTaskDelay( xDelay5ms )设置了5个TICK时间,系统每隔一个TICK时间就会检查vTask3是否可以恢复运行,经过5个TICK时间后,在7的位置,恢复vTask3寄存器现场,vTask3再次运行。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式FreeRTOS学习九,任务链表的构成,TICK时间中断和任务状态切换调度 的相关文章

  • stm32---红外接受

    一个脉冲对应 560us 的连续载波 xff0c 一个逻辑 1 传输需要 2 25ms xff08 560us 脉冲 43 1680us 低电平 xff09 xff0c 一个逻辑 0 的传输需要 1 125ms xff08 560us 脉冲
  • printf重定向

    C语言中printf默认输出设备是显示器 xff0c 当开发板没有时我们就用串口来打印数据 int fputc int ch FILE p USART SendData USART1 ch 如果用串口2打印 xff0c 和换成USART2
  • SPI的CRC校验计算

    22 3 6 CRC计算 CRC校验仅用于保证全双工通信的可靠性 数据发送和数据接收分别使用单独的CRC计算器 通过对每一个接收位进行可编程的多项式运算来计算CRC CRC的计算是在由SPI CR1寄存器 中CPHA和CPOL位定义的采样时
  • 记录JPA并发save时遇到的坑

    前言 在JPA中 xff0c 使用save方法时是这样的 xff1a 如果我们save的对象指定了主键 xff0c 那么会根据主键先进行一次查询 xff0c 如果查询记录不存在则执行insert语句 xff0c 如果查询记录存在则执行upd
  • Openmv(一)OpenMV图像处理的基本方法

    一 图像处理基础知识 摄像头 xff1a 光学信号转换成电信号 计算机视觉中 xff0c 最简单的模型是小孔成像模型 小孔成像是一种理想模型 xff0c 实际镜头会存在场曲和畸变等 xff0c 但可以通过在标定过程中引入畸变参数解决 xff
  • CMakeLists详解

    CMakeLists详解 一 CMake简介 cmake 是一个跨平台 开源的构建系统 它是一个集软件构建 测试 打包于一身的软件 它使用与平台和编译器独立的配置文件来对软件编译过程进行控制 二 常用命令 1 指定cmake最小版本 cma
  • c++继承与多态总结

    不知不觉C 43 43 课程的学习已经接近尾声 xff0c 感觉自己对于c 43 43 的认知更近了一步 xff0c 粗略总结一下最近学习的继承与多态部分的知识 继承 C 43 43 的继承 继承有3种形式 xff1a 私有继承 保护继承
  • C++对象的销毁

    对象的销毁 一般来说 xff0c 需要销毁的对象都应该做清理 解决方案 1 为每个类都提供一个public的free函数 xff1b 2 对象不再需要时立即调用free函数进行清理 析构函数 1 C 43 43 的类中可以定义一个特殊的清理
  • C++中类中的函数重载

    类中的函数重载 函数重载的回顾 1 函数重载的本质就是为相互独立的不同函数 xff1b 2 C 43 43 中通过函数名和函数参数确定函数调用 xff1b 3 无法直接通过函数名得到重载函数的入口地址 xff1b 4 函数重载必然发生在同一
  • C++中的字符串类

    字符串类 历史遗留的问题 1 C语言不支持真正意义上的字符串 xff1b 2 C语言用字符数组和一组实现字符串操作 xff1b 3 C语言不支持自定义类型 xff0c 因此无法获得字符类型 xff1b 解决方案 1 从C到C 43 43 的
  • MySQL中的Block Nested Loop优化分析

    前言 一般在MySQL规范中 xff0c 都会规定如果两张表进行join查询 xff0c 那么join的字段一定要有索引 xff0c 在之前的文章中我们分析了MySQL join大小表前后顺序影响分析 xff0c 这是在有索引的情况下 xf
  • C++之类模板的概念和意义

    类模板 一些类主要用于存储和组织数据元素 类中数据组织的方式和数据元素的具体类型无关 如 xff1a 数组类 链表类 Stack Queue类 等 1 C 43 43 中将模板的思想应用于类 xff0c 使得类的实现不关注数据元素的具体类型
  • C++之单例类模板

    需求的提出 在架构设计时 xff0c 某些类在整个系统生命周期中最多只能有一个对象存在 xff08 Single Instance xff09 要控制类的对象数目 xff0c 必须对外隐藏构造函数 xff1b 思路 xff1a 1 将构造函
  • 【无标题】

    绘图控件GraphicsView 一 GraphicsView简介 1 QT有多种绘图相关的技术 xff0c 我们将在第2部分 2 4 QT绘图和图表 中比较详细系统的讲 2 本节简单讲一下GraphicsView的基本理论 xff0c 并
  • uboot源码分析之start.S解析

    1 start S引入 1 1 u boot lds中找到start S入口 1 在uboot中因为有汇编阶段参与 xff0c 因此不能直接找main c 整个程序的入口取决于链接脚本中ENTRY声明的地方 ENTRY start 因此 s
  • uboot启动第二阶段

    uboot启动第二阶段 start armboot函数简介 一个很长的函数 1 这个函数在uboot lib arm board c的第444行开始到908行结束 2 450行还不是全部 xff0c 因为里面还调用了别的函数 3 为什么这么
  • cmake设置编译类型为release命令

    cmake编译类型通常默认为debug xff0c 但是在编译软件时 xff0c 一般都需要使用release版本的 xff0c debug太慢了 设置为release版本可以在cmake文件里进行 xff0c 也可以在运行cmake命令时
  • 设计模式之单例模式(Singleton),以C++为例,实现日志输出。

    Hello大家好 xff0c 好久没更新了 xff0c 今天给大家补上最基础的设计模式 xff1a 单例模式 这个单例模式实在是我的心结啊 xff0c 2021年末左右面试京东算法岗 xff0c 面试官让我写一个单例 xff0c 没写出来
  • 源码分析MyBatis对数值(int、double)类型进行test判断的误区

    文章目录 问题描述问题分析验证解析表达式执行解析后表达式分别测试两个条件 查询Ognl官方文档验证问题解决 问题描述 在如下判断中 xff0c 如果type类型为int xff0c 那么对于type 61 39 39 部分判断会出现一些问题
  • Git报错:error: xxxx bytes of body are still expected.

    git一个很老的项目 xff0c 项目深度很深 xff0c 报错 xff1a error 7857 bytes of body are still expected fetch pack unexpected disconnect whil

随机推荐

  • 设计模式之代理模式(Proxy),以C++为例,实现远程代理、虚拟代理、保护代理等。

    兄弟姐妹们好 xff0c 又是好久没有更新了 xff0c 今天给大家简单介绍代理模式 xff0c 一个很简单的设计模式 xff0c 旨在不改变原对象的情况下通过代理对象来控制对原对象的访问 代理模式根据具体情况还可以分为远程代理 虚拟代理
  • C++ 互斥锁原理以及实际使用介绍

    兄弟姐妹们 xff0c 我又回来了 xff0c 今天带来实际开发中都需要使用的互斥锁的内容 xff0c 主要聊一聊如何使用互斥锁以及都有哪几种方式实现互斥锁 实现互斥 xff0c 可以有以下几种方式 xff1a 互斥量 xff08 Mute
  • 【C++】使用【windwos api】获取windwos计算机的基本信息

    今天来一篇获取windows计算机的基本信息的文章 xff0c 包含计算机名称 操作系统版本 处理器信息 内存信息 硬盘信息 显示器信息 网络信息 驱动程序信息 电源信息 其他硬件信息 目录 一 windwos系统包含的基本信息 二 获取信
  • C++ POCO库的基础介绍(Windwos和Linux)

    简单介绍C 43 43 POCO库能干什么 xff0c 后续有时间的话将根据其每个点详细解析 xff0c 关注我 本篇包含POCO库简单介绍 下载以及安装方式 简单代码示例 目录 一 POCO简单介绍 1 1 POCO库的基本模块 1 2
  • ROS踩坑记录

    ROS踩坑记录 问题 xff1a ubuntu 没有 dev ttyUSB0问题 xff1a 运行 launch 文件或 ROS 节点时出现 exit code 9 错误提示问题 xff1a windows使用vscode远程连接 xff0
  • STM32串口数据接收 --环形缓冲区

    STM32串口数据接收 环形缓冲区 环形缓冲区简介 在单片机中串口通信是我们使用最频繁的 xff0c 使用串口通信就会用到串口的数据接收与发送 xff0c 环形缓冲区方式接收数据可以更好的保证数据丢帧率第 在通信程序中 xff0c 经常使用
  • 如何设计安全可靠的开放接口---对请求参加密保护

    文章目录 如何设计安全可靠的开放接口 系列前言AES加解密代码实现 如何设计安全可靠的开放接口 系列 1 如何设计安全可靠的开放接口 之Token 2 如何设计安全可靠的开放接口 之AppId AppSecret 3 如何设计安全可靠的开放
  • rosdep init报错解决方法

    rosdep init报错解决方法 很多小伙伴在安装ROS的过程中都不可避免的要执行rosdep init和rosdep update这两行命令行 xff0c 这也是在安装ROS的过程中最让人头疼的两步 xff0c 一般都没法一次成功 xf
  • NVIDIA Jetson Nano/Xavier NX 扩容教程

    在售的 NVIDIA Jetson 内置 16 GB 的 eMMC xff0c 并已安装了 ubuntu 18 04 LTS 和 NVIDIA JetPack 4 6 xff0c 所以剩余的用户可用空间大约 2GB xff0c 这对将 NV
  • 深度学习框架YOLOv3的C++调用

    深度学习框架YOLOv3的C 43 43 调用 深度学习框架YOLOv3的C 43 43 调用 xff08 1 xff09 tensorflow版本的YOLOv3的C 43 43 调用 xff08 失败 xff09 xff08 2 xff0
  • 基于GPT-2实现图像文本生成

    原理 使用GPT 2模型处理文本 xff0c 做decoder 使用google的vit base patch16 224模型处理图像 xff0c 做encoder 最后通过VisionEncoderDecoderModel将这两个模型粘起
  • C语言中常见的两个比较字符串是否相等的函数strcmp和strncmp

    函数 xff1a strcmp和strncmp strcmp 使用格式 xff1a include lt string h gt int strcmp const char s1 const char s2 设这两个字符串为str1 xff
  • sprintf和printf 用法的区别

    printf 的作用是标准化输出 xff0c 默认的对象是标准输出缓冲区 xff0c 要有一定的条件才能把缓冲区里面的数据输出 sprintf 作用是格式化输出函数 xff0c 保存字符串到缓冲区中 xff0c 起到拼接字符串的作用 功能
  • 第六篇,STM32脉冲宽度调制(PWM)编程

    1 PWM概念 PWM叫脉冲宽度调制 Pulse Width Modulation xff0c 通过编程控制输出方波的频率和占空比 高低电平的比例 xff0c 广泛应用在测量 xff0c 通信 xff0c 功率控制等领域 呼吸灯 xff0c
  • 第十篇,STM32串口蓝牙编程

    1 串口蓝牙概念 串口蓝牙是一个蓝牙模块 xff0c 内部有蓝牙模块和程序 xff0c 可以进行蓝牙通信 xff0c 同时提供一个串口接口 xff0c 通过串口可以配置蓝牙模块进行数据传输 2 使用串口3连接蓝牙模块 3 手机上安装蓝牙调试
  • LeetCode岛屿问题通用解决模板

    文章目录 前言第一题 xff1a 求岛屿的周长模板整理遍历方向确定边界重复遍历问题处理 模板解第一题第二题 xff1a 求岛屿数量第三题 xff1a 岛屿的最大面积第四题 xff1a 统计子岛屿第五题 xff1a 统计封闭岛屿的数目第六题
  • 第十四篇,STM32的CAN总线通信

    1 CAN总线的概念 CAN指的是控制器局域网网络 Controller Area Network xff0c 由德国博世汽车电子厂商开发出来 CAN使用差分信号 xff0c 具有较强的抗干扰能力和传输稳定性 CAN属于多主通信 xff0c
  • OpenCV图像处理学习十九,像素重映射cv::remap

    一 像素重映射概念 重映射就是把输入图像中各个像素按照制定的规则顺序映射到另外一张图像的对应位置上去 xff0c 形成一张新的图像 二 像素映射API函数接口 cv remap xff08 InputArray src 输入图像 Outpu
  • OpenCV图像处理学习二十一,直方图比较方法

    一 直方图比较 直方图比较是对输入的两张图像进行计算得到直方图H1与H2 xff0c 归一化到相同的尺度空间 xff0c 然后可以通过计算H1与H2的之间的距离得到两个直方图的相似程度 xff08 每张图像都有唯一的直方图与之对应 xff0
  • 嵌入式FreeRTOS学习九,任务链表的构成,TICK时间中断和任务状态切换调度

    一 tskTaskControlBlock 函数结构体 在tskTaskControlBlock 任务控制块结构体中 xff0c 其中有任务状态链表和事件链表两个链表成员 xff0c 首先介绍任务状态链表这个结构 xff0c 这个链表通常用