FreeRTOS学习第三篇——FreeRTOS任务创建(下)

2023-05-16

声明:本文为博主的学习篇章,欢迎大家指错,共同学习
在解决一下上篇遗留下来的问题之前,还得提前做些功课,了解一些FreeRTOS的全局变量。

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;						/* 调度器挂起时已就绪的任务。当调度器恢复时,它们将被移到就绪列表中 */

#if( INCLUDE_vTaskDelete == 1 )

	PRIVILEGED_DATA static List_t xTasksWaitingTermination;				/* 已被删除的任务-但它们的内存尚未释放。 */
	PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U;

#endif

#if ( INCLUDE_vTaskSuspend == 1 )

	PRIVILEGED_DATA static List_t xSuspendedTaskList;					/* 当前挂起的任务。 */

#endif

在FreeRTOS中,每个任务都有一个状态(就绪、挂起、延时等),所以FreeRTOS就用任务状态列表项来表述该任务的状态。具体是这样的:FreeRTOS会创建一些状态列表,如就绪任务列表,挂起任务列表等,任务处于什么状态就将任务状态列表项挂在哪个列表项。就绪任务列表是一个列表数组,每个不同的优先级享有独立的就绪任务列表,也就是说pxReadyTasksLists[0]是专门给优先级为0的任务挂载的。configMAX_PRIORITIES在文件FreeRTOSConfig.h中定义,描述了FreeRTOS最高的优先级。
当任务因为vTask_Delay等阻塞性堵塞函数暂时无法继续运行下去时,任务状态列表项会被挂在延时任务列表下,表示当前任务在等待阻塞时间结束,并且任务状态列表项中的值会被赋值为阻塞时间。之前有说过,列表项插入列表是根据列表项的值进行升序排序的,也就是说延时列表下挂的第一个列表项永远都是阻塞时间最短的任务。
pxDelayedTaskList用于指向当前的延时任务列表。这里有两个延时任务列表和两个指向延时任务列表的指针。对于一个计数器来说,一直加上去的话总有溢出的时候,系统节拍计数器也不例外,当计数器溢出后计数值会从重装载值重新开始计数。其实当一个任务被阻塞,设定了一个阻塞时间时,其任务状态列表项的值并不是一个需要等待多少时间的值,而是等待时间加上系统计数器当前的计数值。

if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
		{
			vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
		}
		else
		{
			xTimeToWake = xConstTickCount + xTicksToWait;

			listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );

所以当系统节拍计数器溢出时,延迟任务列表下挂的所有任务项的值都需要进行特殊处理,所以就会有两个延时任务列表和两个指针。
xPendingReadyList用于保存已经就绪,但是任务调度器未开启,暂时无法运行的任务状态列表项。根据经验来说这里的任务状态列表项的值应该是任务的优先级,再根据列表项插入列表的API实现来看,此列表的第一项永远都是优先级最高任务状态列表项,对于相同优先级的任务状态列表项,最先进来的列表项排在前面,这也比较符合正常思维。

对于上面所说的全局变量在第一次创建任务的时候需要对这些变量进行初始化。

static void prvInitialiseTaskLists( void )
{
UBaseType_t uxPriority;

	for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
	{
		vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
	}

	vListInitialise( &xDelayedTaskList1 );
	vListInitialise( &xDelayedTaskList2 );
	vListInitialise( &xPendingReadyList );

	#if ( INCLUDE_vTaskDelete == 1 )
	{
		vListInitialise( &xTasksWaitingTermination );
	}
	#endif

	#if ( INCLUDE_vTaskSuspend == 1 )
	{
		vListInitialise( &xSuspendedTaskList );
	}
	#endif 

	pxDelayedTaskList = &xDelayedTaskList1;
	pxOverflowDelayedTaskList = &xDelayedTaskList2;
}

这个函数非常简单,就是调用了之前说过的列表初始化函数。

接着就该继续上一篇遗漏的知识了

将任务加入到就绪任务列表中

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
	taskENTER_CRITICAL();						/* 进入临界区 */	
	{
		uxCurrentNumberOfTasks++;				/* 任务统计数量加一 */
		if( pxCurrentTCB == NULL )
		{
			pxCurrentTCB = pxNewTCB;			/* 如果当前没有任务在运行,就将此任务赋给pxCurrentTCB */

			if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
			{
				prvInitialiseTaskLists();		/* 如果当前任务的数量为一,即第一次创建任务就初始化一些列表 */
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			if( xSchedulerRunning == pdFALSE )	/* 如果没有打开调度器 */
			{
				/* 如果新创建的任务优先级大于变量pxCurrentTCB指向的任务优先级,则设置pxCurrentTCB指向当前新创建的任务TCB(确保pxCurrentTCB指向优先级最高的就绪任务) */ 
				if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
				{
					pxCurrentTCB = pxNewTCB;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
			
		uxTaskNumber++;						/* 任务控制块的编号加一 */

		#if ( configUSE_TRACE_FACILITY == 1 )	/* 可视化追踪功能 */
		{
			pxNewTCB->uxTCBNumber = uxTaskNumber;
		}
		#endif
		traceTASK_CREATE( pxNewTCB );

		prvAddTaskToReadyList( pxNewTCB );		/* 添加任务到就绪列表中 */

		portSETUP_TCB( pxNewTCB );				/* 将pxNewTCB设置为空 */
	}
	taskEXIT_CRITICAL();						/* 退出临界区 */

	if( xSchedulerRunning != pdFALSE )
	{	/* 如果创建的任务的优先级高于当前任务,那么现在应该运行它 */
		if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
		{
			taskYIELD_IF_USING_PREEMPTION();	/* 进行任务切换 */
		}
		else 
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

首先是进入临界区,什么是临界区呢,这里又是怎么进入的?
临界区指的是那些必须完整运行,中途不能被打断的代码段。也就是说,在运行这段代码的时候不能切换任务,也不能让中断打断。所以在运行这段代码之前要先进入临界区,运行完后退出临界区就能够确保这段代码运行中途不被打断。如何进入临界区呢?答案就是关中断。这里补充一个知识点,在FreeRTOS中,所有任务的切换都是在PendSV中断函数里进行的,也就是说,要进行任务切换就必须发起一次PendSV中断。在FreeRTOS中,所有中断的优先级都高于最高优先级的任务,所以所有中断都可以打断任务的运行。所以关闭中断也就关闭了任务之间的切换。退出临界区就是开中断啦。
uxCurrentNumberOfTasks是一个全局变量,用于记录当前的任务数量(创建了未删除的所有任务)。
pxCurrentTCB也是一个全局变量,用于记录当前正在执行的任务。
如果调度器还没打开(程序刚开始运行时,可能会先创建几个任务,之后才会启动调度器),并且新创建的任务优先级大于变量pxCurrentTCB指向的任务优先级,则设置pxCurrentTCB指向当前新创建的任务TCB(确保pxCurrentTCB指向优先级最高的就绪任务)。
第一次创建任务需要对一些列表进行初始化。
uxTaskNumber用于记录任务数量变化的次数,每次任务数量发生了变化,这个值就会加一(任务创建和任务删除)。
prvAddTaskToReadyList是在task.c中的一个宏定义

#define prvAddTaskToReadyList( pxTCB )																\
	traceMOVED_TASK_TO_READY_STATE( pxTCB );														\
	taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );												\
	vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
	tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )

taskRECORD_READY_PRIORITY用于将此任务的优先级保存到就绪任务优先级的变量中。vListInsertEnd就是列表项的尾部插入操作了。

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

FreeRTOS学习第三篇——FreeRTOS任务创建(下) 的相关文章

随机推荐

  • 算法工程师的自我修养(AlgorithmEngineerPrepares)

    算法工程师的自我修养 整理一份算法工程师的最小必需的知识框架 xff0c 内容包括 计算机视觉 机器学习基础 深度学习基础 数据处理分析 矩阵论 信息论 NLP基础 xff0c 持续更新中 xff0c 欢迎关注 地址 xff1a https
  • 从零开始实现一个简单的CycleGAN项目

    项目地址 xff1a https github com jzsherlock4869 cyclegan pytorch pytorch 中CycleGAN xff08 循环一致生成对抗网络 xff09 的简单且易于修改的实现 CycleGA
  • 【计算机视觉】一个简单易上手的图像分类任务pipeline代码

    整理了一个针对图像分类任务的pipeline xff0c 主要用于快速开始验证现有模型结果 xff0c 以及后续的自定义模型的开发 https github com jzsherlock4869 image classification p
  • MySQL存储数据加密

    加密方式主流的有两种 ENCODE 与 DECODE 建一张测试表 create table users username varchar 128 用户昵称 password blob 密码 engine 61 innodb default
  • 线程池BUG复现和解决

    逻辑很简单 xff0c 线程池执行了一个带结果的异步任务 但是最近有偶发的报错 xff1a java util concurrent RejectedExecutionException Task java util concurrent
  • 移动端开发——APP端上H5容器化建设

    1 背景 当前移动端和前端的结合愈加紧密 xff0c 尤其是在偏重活动运营的电商App中 xff0c 受制于App版本审核 xff0c 具备研发成本低 可灵活发布等特点的H5页面受到青睐 xff0c 使其在APP端上承接了越来越多的业务 然
  • C++时间与字符串转换

    1 常用的时间存储方式 1 xff09 time t类型 xff0c 这本质上是一个长整数 xff0c 表示从1970 01 01 00 00 00到目前计时时间的秒数 xff0c 如果需要更精确一点的 xff0c 可以使用timeval精
  • 解决linux环境下nohup: redirecting stderr to stdout问题

    在生产环境下启动Weblogic时 xff0c 发现原来好好的nohup信息输出到指定文件中的功能 xff0c 突然出问题了 现象是控制台输出的信息一部分输出到了我指定的文件 xff0c 另一部分却输出到了nohup out xff0c 而
  • [转]Redis作为消息队列与RabbitMQ的性能对比

    周末测试了一下RabbitMQ的性能 xff0c RabbitMQ是使用Erlang编写的一个开源的消息队列 xff0c 本身支持很多的协议 xff1a AMQP xff0c XMPP SMTP STOMP xff0c 也正是如此 xff0
  • Python3.7 实现TCP通信

    TCP 连接程序分为服务端和客户端两部分 服务端步骤如下 xff1a 1 创建套接字Socket 什么是套接字 应用层通过传输层进行数据通信时 xff0c TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题 为了区别不同的应用程序
  • CentOS、Ubuntu、Debian三个linux比较异同

    Linux有非常多的发行版本 xff0c 从性质上划分 xff0c 大体分为由商业公司维护的商业版本与由开源社区维护的免费发行版本 商业版本以Redhat为代表 xff0c 开源社区版本则以debian为代表 这些版本各有不同的特点 xff
  • LDAP 中 CN, OU, DC 的含义

    1 LDAP的存储规则 区分名 xff08 DN xff0c Distinguished Name xff09 和自然界中的树不同 xff0c 文件系统 LDAP 电话号码簿目录的每一片枝叶都至少有一个独一无二的属性 xff0c 这一属性可
  • bat修改hosts文件

    attrib R C WINDOWS system32 drivers etc hosts 64 echo 64 echo 127 0 0 1 aaaa bbb com gt gt C WINDOWS system32 drivers et
  • 使用org.apache.tools.zip实现zip压缩和解压

    import java io import org apache tools zip import java util Enumeration 功能 zip压缩 解压 支持中文文件名 说明 本程序通过使用Apache Ant里提供的zip工
  • freeModbus代码解读及移植笔记

    freeModbus的代码库还是很好用的 xff0c 本人在wince和C8051F410下均移植成功 xff08 只用到RTU模式 xff09 但freeModbus提供的文档比较少 xff0c 只能对照着Modbus协议一点点试着读懂源
  • MySQL变量:local_infile

    local infile服务器变量指示能否使用load data local infile命令 该变量默认为ON 该变量为OFF时 xff0c 禁用客户端的load data local infile命令 Sql代码 mysql gt sh
  • strcpy函数实现

    C语言标准库函数strcpy的一种典型的工业级的最简实现 返回值 xff1a 返回目标串的地址 对于出现异常的情况ANSI C99标准并未定义 xff0c 故由实现者决定返回值 xff0c 通常为NULL 参数 xff1a strDesti
  • C++库介绍

    1 C 43 43 标准库 xff08 STL xff09 STL六大组件 容器 算法 迭代器 仿函数 适配器 配接器 空间配置器 1 容器 各种数据结构 xff0c 如vector list deque set map等 xff0c 用来
  • 【C++】extern “C“ 用法详解

    前言 前面简单了解了C 43 43 中的extern 34 C 34 之后 xff0c 可能很多小伙伴对这个陌生的词非常困惑 xff0c 不能理解他的使用场景 所以本章内容就来详细了解extern 34 C 34 的用法 xff0c 这里使
  • FreeRTOS学习第三篇——FreeRTOS任务创建(下)

    声明 xff1a 本文为博主的学习篇章 xff0c 欢迎大家指错 xff0c 共同学习 在解决一下上篇遗留下来的问题之前 xff0c 还得提前做些功课 xff0c 了解一些FreeRTOS的全局变量 PRIVILEGED DATA stat