FreeRTOS之信号量和互斥量

本文详细介绍了FreeRTOS中信号量和互斥量的作用,包括为何需要它们,如何通过二值信号量实现同步和互斥,以及优先级继承和递归锁的使用。通过示例展示了信号量在任务同步和资源保护中的应用。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

我可以通过队列来实现同步和异步,为什么还需要一个信号量呢?使用信号量的优点在哪呢,为什么需要互斥量呢?互斥量的优点在哪呢?


一、信号量

本文主要讲述二值信号量。

为什么需要信号量来实现同步

	1、简单性和效率:本文所说的二值信号的机制简单,只需要一个计数器就可以表示资源是否可用,信号量的获取和释放不需要像队列一样创建、拷贝、存储和删除等操作,效率更高。
	2、资源的管理:非常适合用于表示有限资源的数量,如资源池中有多少资源可用。而对于那些不需要传递任何数据,只是单纯想同步任务执行顺序的情况,信号量是一个很好的选择。

什么是二值信号量

二值信号量是一种基础的同步机制,它是一种特殊的信号量,其计数值只能为0或1。二值信号量主要用于表示一个特定的资源是否可用,或者某个条件是否满足。当二值信号量的值为1时,表示资源可用或某个条件为真;当其值为0时,则表示资源不可用或条件未满足。
在利用队列的时候我们通过给队列发送,当任务结束之后再释放。同样的道理。在信号量中我们也要获取(Take)二值信号量和给予(give)二值信号量
**

  • xSemaphoreTake() 的函数尝试获取二值信号量时,如果信号量的值为1,则将其值设置为0,并允许任务继续执行;如果信号量的值为0,则任务将被阻塞,直到其他任务释放该信号量。
  • xSemaphoreGive() 的函数时,信号量的值从0变为1,表示资源现在可用。如果有其他任务因为等待这个信号量而被阻塞,那么系统会根据调度策略解阻塞其中一个等待的任务。

如何通过信号量来实现同步

创建两个任务和一个句柄

static SemaphoreHandle_t xSemCalc;
void Task1Function(void * param)
{
	volatile int i = 0;
	while (1)
	{   
		for (i = 0; i < 10000000; i++)
			sum++;
		//printf("1");
		xSemaphoreGive(xSemCalc);
		vTaskDelete(NULL);
	}
}

void Task2Function(void * param)
{
	while (1)
	{
		//if (flagCalcEnd)
		flagCalcEnd = 0;
		xSemaphoreTake(xSemCalc, portMAX_DELAY);
		flagCalcEnd = 1;
		printf("sum = %d\r\n", sum);
	}
}
主函数
	xSemCalc = xSemaphoreCreateCounting(10, 0);
	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

在这里插入图片描述可以看到变量flagCalcEnd变1的时间和利用队列来实现同步所用时间差不多。

如何通过二值信号量来实现互斥

首先编写一段任务函数、定义一个UART句柄

static SemaphoreHandle_t xSemUART;
void TaskGenericFunction(void * param)
{
	while (1)
	{
		xSemaphoreTake(xSemUART, portMAX_DELAY);
		printf("%s\r\n", (char *)param);
		xSemaphoreGive(xSemUART);
		vTaskDelay(1);
	}
}

主函数

	xSemUART = xSemaphoreCreateBinary();
	xSemaphoreGive(xSemUART);
	xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
	xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);
  我们来看看结果,Task4和Task3是否实现互斥

在这里插入图片描述 实验现象和利用队列实现同步一样。

二、互斥量

为什么需要互斥量来实现互斥

在利用队列来实现互斥的时候我们是需要在初始化的时候手动给队列初始值,但是如果使用互斥量我们无需初始值

在这里插入图片描述可以看到在内部已经实现队列的初始化,所以我们只需要创建就可以。
调用 xSemaphoreCreateMutex() 函数时,系统会创建一个新的互斥量实例,并初始化为未锁定状态。其他任务可以通过调用

  • xSemaphoreTake() 函数来获取(锁定)此互斥量,获取成功的任务可以访问受保护的资源;
  • xSemaphoreGive() 函数来释放(解锁)互斥量,以便其他任务可以获取并访问资源。

实现程序及现象

任务函数

void TaskGenericFunction(void * param)
{
	while (1)
	{
		xSemaphoreTake(xSemUART, portMAX_DELAY);
		printf("%s\r\n", (char *)param);
		xSemaphoreGive(xSemUART);
		vTaskDelay(1);
	}
}

主函数

	xSemUART = xSemaphoreCreateMutex();
	xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
	xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);

实验现象
在这里插入图片描述

使用互斥量的好处

我们通过一个具体的例子来了解使用互斥量的好处,假设我们创建了三个任务,三个优先级分别是高、中、低,程序从高任务开始执行,高任务执行之后故意Delay一段时间,让中优先级任务开始工作,中优先级任务做完delay一段时间,让低优先级的任务做,这个时候高任务唤醒执行后继续delay,此时低任务开始运行,但是当中优先级任务delay结束,抢夺资源那么以后得CPU资源就会一直被中优先级的任务所占有。这就是优先级反转。
在这里插入图片描述

优先级继承

那么使用互斥量的好处就来了,互斥量中我们可以优先级继承
在上述想象中,如果我们引入互斥量,当高任务执行完之后会将优先级继承给低任务,当高任务唤醒后开始执行那么就不会受到中优先级任务的影响。
在这里插入图片描述

递归锁

我们通过一个具体的例子来了解使用互斥量的好处,假设我们有三个任务,任务3任务4正常打印,任务5捣乱强行解锁,那么会发生什么现象?

void TaskGenericFunction(void * param)
{
	int i;
	while (1)
	{
		xSemaphoreTakeRecursive(xSemUART, portMAX_DELAY);

		printf("%s\r\n", (char *)param);
		for (i = 0; i < 10; i++)
		{
			xSemaphoreTakeRecursive(xSemUART, portMAX_DELAY);
			printf("%s in loop %d\r\n", (char *)param, i);
			xSemaphoreGiveRecursive(xSemUART);
		}
		
		xSemaphoreGiveRecursive(xSemUART);
		vTaskDelay(1);
	}
}
void Task5Function(void * param)
{
	vTaskDelay(10);
	while (1)
	{
		printf("%s\r\n", (char *)param);
		xSemaphoreGiveRecursive(xSemUART);
		vTaskDelay(1);
	}
}

在这里插入图片描述可以看到任务5的捣乱影响了任务3和任务4的正常运行。
当我们利用递归锁之后就可以看到程序正常执行。

void Task5Function(void * param)
{
	vTaskDelay(10);
	while (1)
	{
		while (1)
		{
			if (xSemaphoreTakeRecursive(xSemUART, 0) != pdTRUE)
			{
				xSemaphoreGiveRecursive(xSemUART);			
			}
			else
			{
				break;
			}
		}
		printf("%s\r\n", (char *)param);
		xSemaphoreGiveRecursive(xSemUART);
		vTaskDelay(1);
	}
}

在这里插入图片描述


总结

以上就是FreeRTOS中的信号量和互斥量的基本使用,只是简单的运用,源码是如何实现还需要花费大量的时间去理解。

  • 33
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值