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

news2025/7/16 7:10:29

一. 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再次运行。

 

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

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

相关文章

CPU、内存、磁盘性能监控

CPU监控 网络由设备、服务器、路由器、交换机和其他网络组件组成。CPU 是网络中所有硬件设备的组成部分。它负责设备的稳定性和性能。企业严重依赖网络,企业硬件的处理能力决定了网络的容量。随着 CPU 功能和硬件的快速发展,组织必须规划其容量并监控其…

成功上岸,刚转行自学Python的小姑娘,每个月入1W+......

我是一名2020年毕业的本科生,大学学的专业是机械设计制造及其自动化。 在大学期间,觉得机械专业实在枯燥无味,没有一点点成就感,每天就是画图纸,测量零件,计算数据,一切都是纸上谈兵。但凡有因…

甲氧基PEG多巴胺DPA-mPEG,Dopamine-mPEG,PEG化的多巴胺具有良好的水溶性

英文名称:mPEG-DPA mPEG-Dopamine 中文名称:甲氧基-聚乙二醇-多巴胺 分子量:1k,2k,3.4k,5k,10k 产地:广州 品牌:为华生物 存储条件:≤-4℃低温干燥保存…

Netty-实验

Netty应用实例-群聊系统 实例要求: (1)编写一个Netty群聊系统,实现服务端和客户端之间的数据简单通讯(非阻塞) (2)实现多人群聊 (3)服务器端:可…

论文阅读笔记 | 三维目标检测——PointRCNN

如有错误,恳请指出。 文章目录1. 背景2. 网络结构2.1 Proposal Generation2.2 Proposal Refinement3. 实验部分3.1 kitti上的测评3.2 消融实验paper:《PointRCNN: 3D Object Proposal Generation and Detection from Point Cloud》文章比较复杂&#xff…

一文详解Redis企业版软件!

一、Redis企业版软件概述 Redis企业版软件(Redis Enterprise)是企业级的数据库软件,也是一款实时数据平台,为全球超过8500家知名企业提供实时数据服务。具有线性可扩展性、高可用性、持久性、备份和恢复、地理分布、分层内存访问…

WhatsApp群发系统-SendWS拓客系统功能后台介绍(五):WhatsApp筛号群发,群发超链

WhatsApp群发系统 基于WhatsApp进行群发功能,将品牌和产品推送给全世界各地的人们或者选择筛选好的用户,进行针对性的群发,提升了品牌和产品的影响力,让更多人了解认识品牌,帮助客户低成本实现WhatsApp营销精准拓客。…

windows和linux可以共用的端口连通性是否丢包测试工具paping

通常我们在系统无论是在windows还是linux,都会使用telnet命令来测试端口的连通性,但此命令只能测试是否通,无法测试是否有丢包或者是否有中断。paping这个工具就应用而生,它可以在多系统环境下进行像ping一样测试。 一、下载&…

【vscode】远程容器内开发python

一、环境 本人的远程开发环境: docker容器miniconda 常用的IDE: pyCharm专业版vsCodeRemote Development插件Python插件 由于pyCharm专业版要么花钱要么破解,我选择了vscode插件的方式,插件都是microsoft出品。 二、使用 服务…

记一道前端高难度面试题

目录 提问:如何让下面的这行代码成立 1.错误原因 2.思路 3.解题 4.小结 提问:如何让下面的这行代码成立 var [a,b] {a:1,b:2} 直接运行会报错,报错信息如下: Uncaught TypeError: {(intermediate value)(intermediate valu…

手柄零件的工艺设计

手柄零件的工艺设计 目录 一、零件的工艺分析及生产类型的确定 1.零件的作用-------------------------------------------------------------------------- 3 2.热处理-------------------------------------------------------------------------------- 3 3.零件的生产类型-…

架构师书籍推荐

讲实话,要看书只能看看架构师思维相关的数据,开拓一下思路就行,看看别人的看法和观念。 架构师需要积累的技术不要从书上来,去官网看他的说明书,一切纯讲技术类的书籍都有滞后性。 正在用的技术要时常关注一下他官网…

数据结构-图的基本概念

目录 完全图无向图有向图路径长度回路或环⭐⭐无向图-->连通图和连通分量⭐⭐有向图-强连通图和强连通分量完全图 无向图 无向图中每两个顶点之间都存在着一条边。 称为完全图 无向完全图包含n(n-1)/2条边。 有向图 有向图每两个顶点之间都存在着方向相反的两条边。 称…

Nature 、cell 双开花-抗氧化剂与氧化应激

癌细胞经常通过癌症转移调控自身的新陈代谢,进而来有效地支持细胞增殖和存活。因此,因恶性肿瘤转移造成的死亡占癌症整体发病的 95%。2019 年 6 月 27 日,国际 TOP 杂志 Nature 在线发表了中科院上海生化与细胞研究所杨巍维课题组与中科院大连…

el-table中显示echarts的趋势折线图(燃尽图)

显示效果:右边的趋势图其实是查询当前行的30天数据量 背景:为了模仿禅道上的燃尽图,但是查看其源码,发现是用php写的,我们想用vue实现 实现步骤:1.先使用el-table画出表格来 注意:此时数据是…

ctfshow 萌新赛 给她

初识: 一开始看到这个题目以为是sql注入,尝试了各种sql注入转义次都注入不了 .git泄露:最后还是看了一下大佬的解题,发现方向就错了,“给她”——“git”,这题的入口是.git泄露。 我是纯小白,…

如何在React项目中使用TypeScript

本文主要记录我如何在React项目中优雅的使用TypeScript,来提高开发效率及项目的健壮性。 项目目录及ts文件划分 由于我在实际项目中大部分是使用umi来进行开发项目,所以使用umi生成的目录来做案例。 . ├── README.md ├── global.d.ts ├── mo…

SpringBoot SpringBoot 开发实用篇 1 热部署 1.2 自动启动热部署

SpringBoot 【黑马程序员SpringBoot2全套视频教程,springboot零基础到项目实战(spring boot2完整版)】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇1 热部署1.2 自动启动热部署1.2.1 问题引入1.2.2 自动启动热部署1.2.…

Nacos2.1.1集群和持久化配置以及Nginx负载均衡分发(重点)

Nacos集群和持久化配置(重点) 1、官网说明 官网文档地址:https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html 对如上图片进行翻译如下 根据下图,需要配置MySQL作为持久化保存Nacos的信息 默认Nacos使用嵌入式数据库实现…

如何在 Visual Paradigm 中创建流程图丨使用教程

让我们看看如何在 Visual Paradigm 中绘制流程图。我们将在这里使用一个非常简单的流程图示例。完成本教程后,您可以扩展示例。 1.从主菜单中选择图表 > 新建。 2.在New Diagram窗口中,选择Flowchart并单击Next。 3.您可以从空图表开始,…