FreeRTOS源码分析-1创建任务

news2024/11/25 2:16:11

目录

1 任务的句柄(结构体)

2 创建任务主要工作

2.1 创建任务初始化源码分析

2.2 任务添加到就绪列表源码分析

2.3任务堆栈的初始化源码分析

问:R0为什么要入栈保存?因为作为函数的第一个传入参数,必须也要保存。

问:为什么要入栈异常返回值?

1 任务的句柄(结构体)

/********结构体指针*******/
typedef struct tskTaskControlBlock
{
 volatile StackType_t *pxTopOfStack;          //任务栈顶
 ListItem_t   xStateListItem;                 //状态列表
 ListItem_t   xEventListItem;                 //事件列表
 UBaseType_t uxPriority;                      //任务优先级
 StackType_t *pxStack;                        //任务栈地址
 char pcTaskName[ configMAX_TASK_NAME_LEN ];  //任务名称
} tskTCB;

2 创建任务主要工作

任务栈分配内存、控制块分配内存,把任务添加到就绪列表。

补充一个知识:M4权威指南4.4.3栈存储

 

2.1 创建任务初始化源码分析

具体工作,把栈空间的高地址分配给栈顶,任务分配的优先级,任务控制块
链接到任务状态表中,任务控制块连接到事件表中,任务堆栈初始化,之后返回任务栈顶。

/*********创建任务********/
//任务创建函数,其中osThreadCreate是CMSIS封装接口
Key_TaskHandle = osThreadCreate(osThread(Key_Task), NULL);

/**
 * @description: osThreadCreate内部是xTaskCreate,创建一个任务线程,任务栈分配内存、控制块分配内存,把任务添加到就绪列表,初始化
 * @param {TaskFunction_t}pxTaskCode 函数指针(函数名)
 * @param {const char *}pcName 任务名称(字符串)
 * @param {const uint16_t} usStackDepth  任务堆栈大小,单位为字
 * @param {void *} const pvParameters 任务传入参数
 * @param {UBaseType_t} uxPriority 任务优先级
 * @param {TaskHandle_t *} const pxCreatedTask 任务句柄
 * @return {const char *}pxTaskCode
 */
BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
						const char * const pcName,
						const uint16_t usStackDepth,
						void * const pvParameters,
						UBaseType_t uxPriority,
						TaskHandle_t * const pxCreatedTask ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
    TCB_t *pxNewTCB;
    BaseType_t xReturn;

	//硬件平台栈增长方式:M4是满减栈,portSTACK_GROWTH = -1
#if( portSTACK_GROWTH > 0 )
	{
		//略
	}
#else /* portSTACK_GROWTH */
	{
	    StackType_t *pxStack;

		/* 任务栈内存分配*/
		pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );

		if( pxStack != NULL )
		{
			/* 任务控制块内存分配 */
			pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */

			if( pxNewTCB != NULL )  
			{
				/* 赋值栈地址 */
				pxNewTCB->pxStack = pxStack;
			}
			else
			{
				/* 释放栈空间 */
				vPortFree( pxStack );
			}
		}
		else
		{
			pxNewTCB = NULL;
		}
	}
#endif /* portSTACK_GROWTH */

	if( pxNewTCB != NULL )
	{
		/*创建任务初始化,把栈空间的高地址分配给栈顶,任务分配的优先级,任务控制块
        链接到任务状态表中,任务控制块连接到事件表中,//任务堆栈初始化,之后返回任
        务栈顶*/
		prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
        //把任务添加到就绪列表中
		prvAddNewTaskToReadyList( pxNewTCB );
		xReturn = pdPASS;
	}
	else
	{
		xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
	}

	return xReturn;
}

2.2 任务添加到就绪列表源码分析




static void prvInitialiseNewTask( 	TaskFunction_t pxTaskCode,
									const char * const pcName,
									const uint32_t ulStackDepth,
									void * const pvParameters,
									UBaseType_t uxPriority,
									TaskHandle_t * const pxCreatedTask,
									TCB_t *pxNewTCB,
									const MemoryRegion_t * const xRegions ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
    StackType_t *pxTopOfStack;
    UBaseType_t x;

	/*计算栈顶的指针*/
	#if( portSTACK_GROWTH < 0 ) //满减栈
	{
        //把栈空间的高地址分配给栈顶
		pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
        //栈对齐-----栈要8字节对齐
		pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); 

		/*检查是否有错误*/
		configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
	}
	#else /* portSTACK_GROWTH */
	{
		//不是满减栈不需要关注
	}
	#endif /* portSTACK_GROWTH */

	/*存储任务名称,即数组拷贝*/
	for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
	{
		pxNewTCB->pcTaskName[ x ] = pcName[ x ];

		if( pcName[ x ] == 0x00 )
		{
			break;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

	/*补齐字符串*/
	pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';

	/*判断任务分配的优先级是否大于最大值*/
	if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
	{
		uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

    //赋值任务优先级
	pxNewTCB->uxPriority = uxPriority;

    //状态表、事件表初始化
	vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
	vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

	/*任务控制块链接到状态表中*/
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );

	/*任务控制块连接到事件表中 */
	listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
	
	#if( portUSING_MPU_WRAPPERS == 1 )
	{
		//不执行
	}
	#else /* portUSING_MPU_WRAPPERS */
	{
        //任务堆栈初始化,之后返回任务栈顶
		pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
	}
	#endif /* portUSING_MPU_WRAPPERS */

    
	if( ( void * ) pxCreatedTask != NULL )
	{
		/*赋值任务的句柄,即任务控制块*/
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

接下来就是分析上面任务堆栈的初始化

2.3任务堆栈的初始化源码分析

补充:资料阅读M4权威指南,第八章节,分析异常处理

为什么分析异常处理?任务调度其实就是通过CPU内核异常处理实现的

 

 

 

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
    //入栈程序状态寄存器
	pxTopOfStack--; 
	*pxTopOfStack = portINITIAL_XPSR;	/* xPSR */

    //入栈PC指针
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	/* PC */

    //入栈LR链接寄存器
    pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR */

    //不需要初始化
	pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */

    //R0作为传参入栈
	*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */

    //异常返回值入栈,返回值是确定程序使用的栈地址是哪一个?MSP PSP
    pxTopOfStack--;
    pxTopOfStack = portINITIAL_EXEC_RETURN;
    
    //不初始化
	pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */

    //最终返回栈顶
	return pxTopOfStack;
}

补充知识:栈帧

程序的写法顺序按照下图所示 

 

问:R0为什么要入栈保存?因为作为函数的第一个传入参数,必须也要保存。

问:为什么要入栈异常返回值?

返回值是确定程序使用的栈地址是哪一个?MSP PSP。

把任务添加到就绪列表这部分暂未分析,后续会结合其他内容分析

prvAddNewTaskToReadyList( pxNewTCB )

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

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

相关文章

spring复习:(37)ProxyFactoryBean之getObject

该工厂bean的getObject代码如下&#xff1a; public Object getObject() throws BeansException {initializeAdvisorChain();if (isSingleton()) {return getSingletonInstance();}else {if (this.targetName null) {logger.info("Using non-singleton proxies with sing…

4.CSS图文样式

考点&#xff1a;line-height为200%时&#xff0c;font-size为40px

第十五章:DenseASPP for Semantic Segmentation in Street Scenes——在街景语义分割中的DenseASPP

0.摘要 语义图像分割是自动驾驶中的基本街景理解任务&#xff0c;在这个任务中&#xff0c;高分辨率图像中的每个像素被归类为一组语义标签。与其他场景不同&#xff0c;自动驾驶场景中的物体呈现出非常大的尺度变化&#xff0c;这给高级特征表示带来了巨大挑战&#xff0c;因为…

IDEA设置显示行号和方法间的分隔符

IDEA设置显示行号和方法间的分隔符 选择File--Settings--Edotor-General-Apperance&#xff0c;勾选上下图中的选项后点击 OK 即可。 每个函数不迷路~~ Show line numbers&#xff1a;显示行数 Show method separators&#xff1a; 显示方法分隔线。

央视赋能,强势出击——方圆出海与《品牌中国》栏目达成战略合作

2023 央视赋能&#xff0c;强势出击 方圆出海 “日前&#xff0c;深圳市方圆出海科技有限公司与《品牌中国》栏目携手&#xff0c;双方正式达成战略合作协议&#xff0c;央视《品牌中国》栏目负责人正式授予方圆出海“《品牌中国》重点推荐品牌”的荣誉称号。 此次签约标志着…

js的this绑定规则以及箭头函数

目录 调用位置默认绑定隐式绑定隐式丢失 显式绑定callapplybind new绑定装箱绑定优先级this规则之外忽略显式绑定间接函数引用 箭头函数 调用位置 从字面意思上来理解&#xff0c;this似乎是指向自己的 然而在JavaScript中&#xff0c;this并不是绑定到自身的 可以看这一个例子…

蓝牙HID模式下输出中文原理简介

目录 前言一、蓝牙和HID简介二、Unicode编码简介三、Windows下alt键code编码输出中文四、蓝牙HID模式下实现在手机上输入中文的原理 前言 最近在使用蓝牙模组&#xff0c;对于蓝牙模组如何输出中文的原理不太清楚&#xff0c;所以找了一些资料简单学习了下&#xff0c;总结如下…

目标检测——FasterRCNN原理与实现

目录 网络工作流程数据加载模型加载模型预测过程RPN获取候选区域FastRCNN进行目标检测 模型结构详解backboneRPN网络anchorsRPN分类RPN回归Proposal层 ROIPooling目标分类与回归 FasterRCNN的训练RPN网络的训练正负样本标记RPN网络的损失函数训练过程实现正负样本设置损失函数 …

Kubernetes 使用 helm 部署 NFS Provisioner

文章目录 1. 介绍2. 预备条件3. 部署 nfs4. 部署 NFS subdir external provisioner4.1 集群配置 containerd 代理4.2 配置代理堡垒机通过 kubeconfig 部署 1. 介绍 NFS subdir external provisioner 使用现有且已配置的NFS 服务器来支持通过持久卷声明动态配置 Kubernetes 持久…

大模型基础知识汇总

本文总结大模型相关基础知识&#xff0c;用于大模型学习入门 &#xff08;持续更新中…&#xff09; 文章目录 NLP 基础知识传统 NLP 知识NLU 与 NLG 各种任务的差异 Transformer 相关知识Pre Norm与Post Norm的区别&#xff1f;Bert 预训练过程手写 transformer 的 attention …

从0到1:跑团小程序开发心得笔记

背景介绍 随着健康意识的兴起&#xff0c;越来越多的人选择加入跑步俱乐部&#xff0c;不仅体验到了运动的乐趣&#xff0c;也感受到了人生的不同色&#xff0c;那么通过小程序&#xff0c;把俱乐部搬到手机上&#xff0c;通过小程序了解俱乐部动态和运动常识&#xff0c;可以…

C++自定义信号和QML的槽函数建立连接

0x00 在C代码在定义一个信号函数&#xff1a;“void sendData2UI(QString msg);”&#xff0c;该函数主要是将接收到的UDP消息发送到QML界面中 #ifndef UDPCLI_H #define UDPCLI_H#include <QObject> #include <QUdpSocket> #include <QString>class UdpCli …

【Netty】NIO基础(三大组件)

文章目录 三大组件Channel & BufferSelector ByteBufferByteBuffer 正确使用姿势ByteBuffer 内部结构ByteBuffer 常见方法分配空间向 buffer 写入数据从 buffer 读取数据mark 和 reset 字符串与 ByteBuffer 互转Scattering ReadsGathering Writes粘包、半包分析 附&#xf…

《啊哈算法》第一章--排序

文章目录 前言一、排序算法二、桶排序三、冒泡排序三、快速排序总结 前言 今年蓝桥杯没有拿到省一&#xff0c;所以就决定沉下心来学习算法&#xff0c;为了使得算法的学习更加稳固&#xff0c;所以就拿起了&#xff0c;最基础的且最经典的一本算法书《啊哈算法》&#xff0c;…

Redis进阶底层原理- 持久化

Redis作为基于内存的缓存数据库&#xff0c;就会存在断电即失的问题&#xff0c;所以数据的持久化是非常重要的。Redis随着版本升级迭代&#xff0c;持久化技术也在不断的升级&#xff0c;&#xff08;从最开始的RDB&#xff0c;到的Redis1.1版本加入AOF&#xff0c;3.0版本支持…

全志F1C200S嵌入式驱动开发(sd卡驱动)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 说是sd卡,其实是micro sd卡,或者称之为tf卡更合适。一般的soc都支持从tf卡启动,所以用tf卡来学习soc、驱动和linux,对新人来说是比较合适的。前面我们已经用sd卡构建了一个类似…

uniapp:针对与富文本解析的几种方法

第一章、富文本的解析方法 1.1 uniapp自带组件&#xff1a;rich-text <rich-text :nodes"nodes"></rich-text> 1.2 v-html <view v-html"item.content"></view> 1.3 uview组件&#xff1a;u-parse <u-parse :content&quo…

学习babylon.js --- [3] 开启https

babylonjs提供WebVR功能&#xff0c;但是使用这个功能得用https&#xff0c;本文讲述如何使用自签名证书来开启https&#xff0c;基于第二篇文章中搭建的工程。 一 生成自签名证书 首先要安装openssl&#xff0c;这个去网上搜下就行了。安装完之后在终端下输入openssl回车可以…

DeepC 实用教程(三)环境数据

目 录 一、前言二、风谱/风剖三、洋流四、波浪4.1 规则波浪4.2 随机波浪谱 五、方向六、海床属性七、位置7.1 创建位置7.2 规则波时域条件7.3 随机波时域条件7.4 波浪散布图7.4.1 散布图分块7.4.2 时域条件 八、参考文献 一、前言 SESAM &#xff08;Super Element Structure A…

y0usef靶场详解

y0usef靶场详解 靶机感悟&#xff1a;对于这个靶机并没有太多的难点&#xff0c;也没有的别多的绊子&#xff0c;就是猜测下一步是什么&#xff0c;耐心的去思考怎么才能进行到下一步。 靶机下载地址&#xff1a;https://download.vulnhub.com/y0usef/y0usef.ova 这个靶机是…