FreeRTOS入门教程(互斥锁的概念和函数使用)

news2024/11/6 19:26:08

文章目录

  • 前言
  • 一、互斥锁的概念
  • 二、互斥锁相关函数
  • 三、使用互斥锁完成同步
  • 四、FreeRTOS中互斥锁存在的问题
  • 五、优先级反转和优先级继承
  • 六、死锁
  • 七、递归锁
  • 总结


前言

本篇文章带大家学习什么是互斥锁,并且学习一下互斥锁中一些函数的使用方法。

一、互斥锁的概念

FreeRTOS中的互斥锁(Mutex)是一种同步机制,用于控制多个任务对共享资源的访问,以确保只有一个任务可以访问该资源,从而避免竞争条件和数据损坏。互斥锁是一种二进制信号量,它只有两个状态:已锁定和未锁定。

二、互斥锁相关函数

1.xSemaphoreCreateMutex():

函数原型:SemaphoreHandle_t x SemaphoreCreateMutex( void );
功能:用于创建一个互斥锁。
返回值:返回一个指向新创建的互斥锁的句柄(handle)。

2.xSemaphoreTake():

函数原型:BaseType_t x SemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
功能:尝试获取互斥锁。如果互斥锁当前可用(未被锁定),则获取成功;否则,任务将被阻塞,直到互斥锁可用或达到超时时间。
参数:
xSemaphore:互斥锁的句柄。
xTicksToWait:等待获取互斥锁的最大时间,可以使用portMAX_DELAY来表示永久等待。
返回值:获取成功时返回pdTRUE,获取失败(超时或出错)时返回pdFALSE。

3.xSemaphoreGive():

函数原型:BaseType_t x SemaphoreGive(SemaphoreHandle_t xSemaphore);
功能:释放互斥锁,允许其他任务获取它。
参数:xSemaphore:互斥锁的句柄。
返回值:成功返回pdTRUE,失败返回pdFALSE。

4.xSemaphoreGiveFromISR():

函数原型:BaseType_t x SemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken);
功能:从中断服务程序中释放互斥锁。
参数:
xSemaphore:互斥锁的句柄。
pxHigherPriorityTaskWoken:一个指向BaseType_t的指针,用于指示是否唤醒了更高优先级的任务。
返回值:成功返回pdTRUE,失败返回pdFALSE。

三、使用互斥锁完成同步

前面的文章使用信号量解决了访问共享资源的问题,这里使用互斥锁也是可以来解决这个问题的。

// 定义互斥信号量
SemaphoreHandle_t mutex;

void Task1Function(void *pvParameters)
{
    while (1)
    {
        // 等待获取互斥信号量
        if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE)
        {
            printf("Task1\n");
            // 释放互斥信号量
            xSemaphoreGive(mutex);
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS); // 任务延时一秒
    }
}

void Task2Function(void *pvParameters)
{
    while (1)
    {
        // 等待获取互斥信号量
        if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE)
        {
            printf("Task2\n");
            // 释放互斥信号量
            xSemaphoreGive(mutex);
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS); // 任务延时一秒
    }
}

xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

// 创建互斥信号量
mutex = xSemaphoreCreateMutex();

四、FreeRTOS中互斥锁存在的问题

在FreeRTOS中没有实现谁上锁就应该由谁来解锁的机制,那么这样的话一个任务上锁后可以被其他的任务解锁并且使用,这个缺陷就可能会导致一系列的问题发生。

示例代码中创建了两个任务,任务1的优先级比任务2高,那么任务1会先获取到互斥量然后进入无限的循环中,任务2随后开始获取互斥量,第一次获取互斥量失败打印失败,获取失败后任务2自行开锁,随后又获取互斥量成功。

运行结果:

在这里插入图片描述

void Task1Function(void * param)
{
		const TickType_t xTicksToWait = pdMS_TO_TICKS(10UL);
		BaseType_t xStatus;
		
		xStatus = xSemaphoreTake(xMutex, portMAX_DELAY);//获取互斥量
		printf("Task1 take the Mutex %s\r\n", \
			(xStatus == pdTRUE) ? "Success" : "Failed");
	
		while(1)
		{
				vTaskDelay(xTicksToWait);
		}
		
}

void Task2Function(void * param)
{
		const TickType_t xTicksToWait = pdMS_TO_TICKS(10UL);
		BaseType_t xStatus;

		xStatus = xSemaphoreTake(xMutex, 0);//获取互斥量
		printf("Task2 take the Mutex %s\r\n", \
			(xStatus == pdTRUE) ? "Success" : "Failed");

		if(xStatus == pdFALSE)//获取互斥量失败,自己开锁
		{
				xSemaphoreGive(xMutex);
		}
		
		xStatus = xSemaphoreTake(xMutex, portMAX_DELAY);//获取互斥量
		printf("Task2 take the Mutex %s\r\n", \
			(xStatus == pdTRUE) ? "Success" : "Failed");

		while(1)
		{
				vTaskDelay(xTicksToWait);
		}
}

xTaskCreate(Task1Function, "Task1", 100, NULL, 2, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

五、优先级反转和优先级继承

在FreeRTOS中,优先级反转和优先级继承是解决多任务系统中的竞争条件和优先级反转问题的两种相关技术。

1.优先级反转(Priority Inversion):

场景:考虑一个具有三个任务的系统:任务A(高优先级)、任务B(中优先级)、和任务C(低优先级)。任务A正在等待一个共享资源,任务B持有该资源,而任务C需要执行并尝试获取资源。

问题:如果任务B持有资源,任务A会被阻塞,但由于任务C的优先级低于任务A,任务C会被调度并阻止任务B的执行,导致任务A被任务C所阻塞,产生了优先级反转问题。

解决方案:优先级反转的解决方案是在任务B持有资源时将其优先级提高到与任务A相同的优先级。这样,任务C不会阻止任务B的执行,任务A会优先执行。一旦任务A完成并释放资源,任务B的优先级将被还原。

2.优先级继承(Priority Inheritance):

场景:同样考虑一个三个任务的系统:任务A(高优先级)、任务B(中优先级)、和任务C(低优先级)。任务A正在等待一个共享资源,任务B持有该资源,而任务C需要执行并尝试获取资源。

问题:与优先级反转类似,如果任务C的优先级低于任务A,任务C会被调度并阻止任务B的执行,导致任务A被任务C所阻塞。

解决方案:优先级继承引入了一个概念,即任务优先级的临时提升。在这种情况下,任务C的优先级将被提升到与任务A相同的优先级,以确保任务A不会被任务C所阻塞。任务B的优先级仍然是中优先级,但任务C的优先级提升使其不会干扰任务A的执行。

在FreeRTOS中,优先级继承通常是默认行为,这意味着在等待共享资源时,任务的优先级会根据需要自动提升,以避免优先级反转问题。这是通过操作系统内核自动管理任务优先级的方式来实现的。任务的优先级将在等待资源期间提高,并在释放资源后还原。

优先级继承和优先级反转是关键的实时系统技术,确保高优先级任务不会被低优先级任务无限期地阻塞,从而提高系统的可靠性和可预测性。

优先级反转示例:

这里创建三个优先级不同的任务,任务1的优先级最高,任务2其次,任务3最低。

这里讲解一下任务的运行流程,首先任务1优先级最高最先运行,使用vTaskDelay阻塞释放CPU,任务2运行,任务2也主动释放CPU,任务3运行此时任务3先获取信号量,然后再去执行一些耗时的操作,导致无法释放信号量,此时任务2继续运行,一段后任务1想要获取到信号量,但是此时信号量被任务3占用,导致任务1无法获取信号量,此时就导致任务2一直运行。

运行效果:

在这里插入图片描述

static SemaphoreHandle_t xSem;

int flag1 = 0;
int flag2 = 0;
int flag3 = 0;

void Task1Function(void * param)
{
		flag1 = 1;
		flag2 = 0;
		flag3 = 0;	
	
		printf("Task1 is Start\r\n");
		
		vTaskDelay(30);
	
		while(1)
		{
				printf("Task1 is Runing\r\n");
				flag1 = 1;
				flag2 = 0;
				flag3 = 0;

				xSemaphoreTake(xSem, portMAX_DELAY);//获取信号量
				flag1 = 1;
				flag2 = 0;
				flag3 = 0;				
				xSemaphoreGive(xSem);//释放信号量
		}
}

void Task2Function(void * param)
{
		flag1 = 1;
		flag2 = 0;
		flag3 = 0;	
	
		printf("Task2 is Start\r\n");
		
		vTaskDelay(10);
		
		while(1)
		{
				flag1 = 0;
				flag2 = 1;
				flag3 = 0;				
		}
}

void Task3Function(void * param)
{
		int i;
		char c = 'A';
		printf("Task3 is Start\r\n");
	
		while(1)
		{
				flag1 = 0;
				flag2 = 0;
				flag3 = 1;

				xSemaphoreTake(xSem, portMAX_DELAY);//获取信号量
				
				for(i = 0; i < 26; i++)
				{
						//执行一些耗时的操作
						printf("%c", c + i);
						flag1 = 0;
						flag2 = 0;
						flag3 = 1;					
				}
				
				xSemaphoreGive(xSem);//释放信号量
				
				vTaskDelay(1);
		}
}
xSem = xSemaphoreCreateBinary();
xSemaphoreGive(xSem);

xTaskCreate(Task1Function, "Task1", 100, NULL, 3, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 2, NULL);
xTaskCreate(Task3Function, "Task3", 100, NULL, 1, NULL);

使用互斥锁解决优先级反转:

这里使用xMutex代替xSem即可完成这个实验。

static SemaphoreHandle_t xMutex;

当任务1无法运行时会将优先级和任务3进行一次交换,当任务3释放完信号量后,信号量交互回来,任务1继续运行。

运行结果:
在这里插入图片描述

六、死锁

死锁(Deadlock):

死锁是指在多任务系统中,多个任务相互等待对方释放资源,从而导致所有任务无法继续执行的状态。这是一种非常危险的情况,因为它会导致系统停滞,无法正常运行。死锁通常发生在以下情况下:

任务之间互斥地访问共享资源(例如,使用互斥锁)。
任务持有一个资源,同时等待另一个任务持有的资源。

死锁示例:

这里创建两个互斥锁A和B,然后创建两个任务:任务1和任务2,任务1先获取互斥锁A,然后释放CPU资源,然后任务2运行,获取互斥锁B,然后释放CPU资源,任务1运行获取互斥锁B,这时任务1获取不到互斥锁B,因为互斥锁B已经被任务2获取,此时任务2运行,获取互斥锁A,也是无法获取的状态,这时任务1和任务2就陷入了互相等待的状态,发生了死锁。

static SemaphoreHandle_t xMutexA;
static SemaphoreHandle_t xMutexB;

void Task1Function(void * param)
{	
		while(1)
		{
				
				xSemaphoreTake(xMutexA, portMAX_DELAY);//获取互斥锁A
				vTaskDelay(1);
				xSemaphoreTake(xMutexB, portMAX_DELAY);//获取互斥锁B
				
				printf("Take A and B is ok\r\n");
				
				xSemaphoreGive(xMutexA);//释放互斥锁A
				xSemaphoreGive(xMutexB);//释放互斥锁B
		}
}

void Task2Function(void * param)
{	
		while(1)
		{
				xSemaphoreTake(xMutexB, portMAX_DELAY);//获取互斥锁B
				vTaskDelay(1);
				xSemaphoreTake(xMutexA, portMAX_DELAY);//获取互斥锁A
				
				printf("Take A and B is ok\r\n");	
			
				xSemaphoreGive(xMutexB);//释放互斥锁B
				xSemaphoreGive(xMutexA);//释放互斥锁A
		}
}

xMutexA = xSemaphoreCreateMutex();
xMutexB = xSemaphoreCreateMutex();

七、递归锁

递归锁是一种互斥锁的扩展,允许同一个任务多次获取同一个互斥锁而不会产生死锁。在标准的互斥锁中,如果一个任务已经获得锁,再次尝试获取锁会导致阻塞,因为互斥锁是互斥的,只允许一个任务持有。

递归锁允许任务在已经持有锁的情况下继续获取锁,每次获取都需要相应的释放。这对于某些算法和任务设计来说很有用,但需要谨慎使用,因为滥用递归锁可能会导致复杂的代码和潜在的问题,例如死锁。

在FreeRTOS中,可以使用递归互斥锁来实现递归锁的功能,通过xSemaphoreCreateRecursiveMutex()创建递归互斥锁,并使用xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()等函数来获取和释放递归锁。

// 创建一个递归互斥锁
SemaphoreHandle_t xRecursiveMutex;

void Task1(void *pvParameters) {
    while (1) {
        // 获取递归锁,可以多次获取
        xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);

        // 临界区代码
        // ...

        // 释放递归锁
        xSemaphoreGiveRecursive(xRecursiveMutex);

        // 继续执行任务
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void Task2(void *pvParameters) {
    while (1) {
        // 获取递归锁,可以多次获取
        xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);

        // 临界区代码
        // ...

        // 释放递归锁
        xSemaphoreGiveRecursive(xRecursiveMutex);

        // 继续执行任务
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

int main(void) {
    // 创建递归互斥锁
    xRecursiveMutex = xSemaphoreCreateRecursiveMutex();

    if (xRecursiveMutex != NULL) {
        // 创建任务
        xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
        xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);

        // 启动FreeRTOS调度器
        vTaskStartScheduler();
    }

    // 如果创建递归互斥锁失败,应采取适当的错误处理措施

    while (1) {
        // 程常不会执行到这里
    }

    return 0;
}

总结

本篇文章就讲解到这里,下篇文章我们继续讲解FreeRTOS入门教程。

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

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

相关文章

修改java项目启动后在jps展示的名称

文章目录 问题在服务器上启动jar包的时候,通过jps查看java进程只展示个 jar解决,指定jar包的全路径jps查看maven打包修改jar名称 问题在服务器上启动jar包的时候,通过jps查看java进程只展示个 jar 解决,指定jar包的全路径 java -jar /root/test/aaa.jarjps查看 494976 aaa.ja…

OpenCV实现物体尺寸的测量

一 &#xff0c;项目分析 物体尺寸测量的思路是找一个确定尺寸的物体作为参照物&#xff0c;根据已知的计算未知物体尺寸。 如下图所示&#xff0c;绿色的板子尺寸为220*300&#xff08;单位&#xff1a;毫米&#xff09;&#xff0c;通过程序计算白色纸片的长度。 主要是通过…

DatenLord前沿技术分享 No.38

达坦科技专注于打造新一代开源跨云存储平台DatenLord&#xff0c;通过软硬件深度融合的方式打通云云壁垒&#xff0c;致力于解决多云架构、多数据中心场景下异构存储、数据统一管理需求等问题&#xff0c;以满足不同行业客户对海量数据跨云、跨数据中心高性能访问的需求。在本周…

WebGIS面试(第二期)

一、简介 近期看到好多小伙伴在寻找WebGIS方面的面试题&#xff0c;正好本人之前也因自己面试分享过一些面试题&#xff0c;秋招目前逐步也快结束了&#xff0c;所以我现在慢慢整理一些包括自己面试以及网上公开的一些我认为有用的面试题&#xff0c;仅供参考&#xff0c;大多…

吃鸡游戏出现msvcp140.dll缺失的四个解决方法

首先&#xff0c;让我们了解一下什么是MSVCP140.dll。MSVCP140.dll是Microsoft Visual C 2015 Redistributable Package的一部分&#xff0c;它是一组C库文件&#xff0c;用于支持使用Visual Studio 2015开发的应用程序。如果你的程序需要这些库文件来正常运行&#xff0c;那么…

结构体内存对齐规则

✨前言✨ &#x1f4d8; 博客主页&#xff1a;to Keep博客主页 &#x1f646;欢迎关注&#xff0c;&#x1f44d;点赞&#xff0c;&#x1f4dd;留言评论 ⏳首发时间&#xff1a;2023年10月20日 &#x1f4e8; 博主码云地址&#xff1a;博主码云地址 &#x1f4d5;参考书籍&…

【量化交易笔记】10.建立最简单的交易策略

概述 量化说得简单一些用策略进行股票交易&#xff0c;在实施交易之前&#xff0c;需要制定策略&#xff0c;并回测试共效果 为了把交易说明清楚&#xff0c;将这个过程&#xff0c;能简单&#xff0c;就简单&#xff0c;总之&#xff0c;简单&#xff0c;简单再简单。 以下主…

NodeJS的初使用,以及引入第三方插件和安装淘宝镜像的教程

NodeJs 命令 npm init -y 生成package.json文件npm i jquery --save–dev 开发依赖(jQuery后面还可以跟模块,可以有多个)npm i jquery --save 生产依赖npm i jquery --D 开发依赖npm uninstall jquery 卸载删除npm i 把删掉的模块,全部重新加载回来 1.介绍 1.什么是NodeJs?…

SystemVerilog Assertions应用指南 Chapter 1.16“ended”结构

1.16“ended”结构 到目前为止,定义的序列都只是用了简单的连接( concatenation)的机制。换句话说,就是将多个序列以序列的起始点作为同步点,来组合成时间上连续的检查。SVA还提供了另种使用序列的结束点作为同步点的连接机制。这种机制通过给序列名字追加上关键词“ ended”来…

python二次开发Solidworks:修改实体尺寸

立方体原始尺寸&#xff1a;100mm100mm100mm 修改后尺寸&#xff1a;10mm100mm100mm import win32com.client as win32 import pythoncomdef bin_width(width):myDimension Part.Parameter("D1草图1")myDimension.SystemValue width def bin_length(length):myDime…

OpenGL —— 2.6、绘制一个正方体并贴图(附源码,glfw+glad)

源码效果 C源码 纹理图片 需下载stb_image.h这个解码图片的库&#xff0c;该库只有一个头文件。 具体代码&#xff1a; vertexShader.glsl #version 330 corelayout(location 0) in vec3 aPos; layout(location 1) in vec2 aUV;out vec2 outUV;uniform mat4 _viewMatrix; u…

手帐怎么做?推荐这10款手帐达人都在用的好用软件!

手帐是一种强大的可视化方式&#xff0c;可以记录你的观察、日常的思考&#xff0c;并随时间的推移来衡量你的进步。在各种手帐软件的帮助下&#xff0c;创建并完全自定义你的手帐变得比以往任何时候都更容易。 有无数的手帐软件可供选择&#xff0c;满足各种需求。但在众多的…

基于Java的勤工助学管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…

京东数据分析:2023年9月京东洗烘套装品牌销量排行榜!

鲸参谋监测的京东平台9月份洗烘套装市场销售数据已出炉&#xff01; 根据鲸参谋平台的数据显示&#xff0c;今年9月份&#xff0c;京东平台洗烘套装的销量为7100&#xff0c;环比下降约37%&#xff0c;同比增长约87%&#xff1b;销售额为6000万&#xff0c;环比下降约48%&#…

使用Spyder进行动态网页爬取:实战指南

导语 知乎数据的攀爬价值在于获取用户观点、知识和需求&#xff0c;进行市场调查、用户画像分析&#xff0c;以及发现热门话题和可能的新兴领域。同时&#xff0c;知乎上的问题并回答也是宝贵的学习资源&#xff0c;用于知识图谱构建和自然语言处理研究。爬取知乎数据为决策和…

如何在3DMAX中使用tyFlow粒子模拟插件创建样条线网格模型

3DMAX粒子模拟插件tyFlow是3dMAX上一款粒子模拟插件&#xff0c;3dMax的粒子流的非官方替代品&#xff0c;类似于Particle Flow插件&#xff0c;但是功能上更加强大&#xff0c;模拟粒子流动、汇聚破碎等各种效果。 tyFlow不仅仅是对粒子流的升级——相反&#xff0c;它是从头…

介绍drawio和图表使用场景

图表介绍 drawio是一个基于Web技术的草图、简图和图表的解决方案。 drawio支持在线编辑器&#xff0c;app.diagram.net.并支持不同的操作系统的桌面版离线安装版本。如&#xff1a;windows, linux, macOS。 对于个人或者团队&#xff0c;把图表绘制的安全放到第一位&#xff…

【马蹄集】—— 概率论专题:第二类斯特林数

概率论专题&#xff1a;第二类斯特林数 目录 MT2224 矩阵乘法MT2231 越狱MT2232 找朋友MT2233 盒子与球MT2234 点餐 MT2224 矩阵乘法 难度&#xff1a;黄金    时间限制&#xff1a;5秒    占用内存&#xff1a;128M 题目描述 输入两个矩阵&#xff0c;第一个矩阵尺寸为 l…

Python-嗨格式 之音频转换,ncm转mp3

缘由&#xff1a;本想下载一些歌到车机播放&#xff0c;发现大部分是ncm格式的。 查了下才知道是音乐软件限制了&#xff0c;会员下载的音频文件为.ncm格式&#xff0c;目前只能在网易云音乐APP上播放&#xff0c;不支持在其他音频播放软件或音频设备使用。 百度转换软件&…

Python词语转拼音

使用python写的图形汉语词语转拼音小工具 1)安装库 pip install flet 2)代码 # 声母列表 initial_consonant_list [b, p, m, f, d, t, n, l, g, k, h, j, q, x, zh, ch, sh, r,z, c, s, y, w] # 韵母列表 list_of_vowels [a, o, e, i, u, , ai, ei, ui, ao, ou, iu, ie, e…