【freertos】FreeRTOS信号量的介绍及使用

FreeRTOS信号量

  • 一、概述
  • 二、PV原语
  • 三、函数接口
    • 1.创建一个计数信号量
    • 2.删除一个信号量
    • 3.信号量释放
    • 4.在中断释放信号量
    • 5.获取一个信号量,可以是二值信号量、计数信号量、互斥量。
    • 6.在中断获取一个信号量,可以是二值信号量、计数信号量
    • 7.创建一个二值信号量
  • 四、示例代码
    • 1、计数型信号量
    • 2、二值信号量-保护共享资源
  • 五、优先级翻转
    • 1、概述
    • 2、解决方案

一、概述

\quad 信号量,Semaphore。
\quad 信号量常用于任务的同步,通过该信号,就能够控制某个任务的执行,这个信号具有计数值,因此,可以称为计数信号量。
\quad 计数信号量可以用于资源管理,允许多个任务获取信号量访问共享资源,但会限制任务的最大数目,初值常为共享资源的数量。访问的任务数达到可支持的最大数目时,会阻塞其他试图获取该信号量的任务,直到有任务释放了信号量。这就是计数型信号量的运作机制,虽然计数信号量允许多个任务访问同一个资源,但是也有限定,比如某个资源限定只能有3个任务访问,那么第4个任务访问的时候,会因为获取不到信号量而进入阻塞,等到有任务(比如任务1)释放掉该资源的时候,第4个任务才能获取到信号量从而进行资源的访问,其运作的机制具体见下图。

在这里插入图片描述

二、PV原语

$\quad$1965年,荷兰学者Dijkstra提出了利用信号量机制解决进程同步问题,信号量正式成为有效的进程同步工具,现在信号量机制被广泛的用于单处理机和多处理机系统以及计算机网络中。
\quad 信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用临界区的进程数。
\quad Dijkstra同时提出了对信号量操作的PV原语。

P原语操作的动作是:
(1)S减1;
(2)若S减1后仍大于或等于零,则进程继续执行;
(3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。

V原语操作的动作是:
(1)S加1;
(2)若相加结果大于零,则进程继续执行;
(3)若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。
PV操作对于每一个进程来说,都只能进行一次,而且必须成对使用。在PV原语执行期间不允许有中断的发生。

\quad 信号量的P、V操作,P表示申请一个资源,每次P操作使信号量减1,V是释放一个资源,每次V操作使信号量加1。信号量表示的是当前可用的资源个数,当信号量为负时,申请资源的进程(任务)就只能等待了。所以,信号量是负的多少,就表明有多少个进程(任务)申请了资源但无资源可用,只能处于等待状态。
\quad 除了访问共享资源外,亦可中断/任务控制某任务的执行,称之为“单向同步”。

三、函数接口

信号量使能,文件路径:freertos.h,另外用户代码要包含semphr.h。

#ifndef configUSE_COUNTING_SEMAPHORES
	#define configUSE_COUNTING_SEMAPHORES 1
#endif

1.创建一个计数信号量

SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);

参数说明:

  • uxMaxCount-计数信号量的最大值,当达到这个值的时候,信号量不能再被释放。
  • uxInitialCount-创建计数信号量的初始值。

返回值:

  • 如果创建成功则返回一个计数信号量句柄,用于访问创建的计数信号量。
  • 如果创建不成功则返回 NULL。

2.删除一个信号量

\quad vSemaphoreDelete()用于删除一个信号量,包括二值信号量,计数信号量,互斥量和递 归互斥量。如果有任务阻塞在该信号量上,那么不要删除该信号量。

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

参数说明:

  • xSemaphore-信号量句柄。

返回值:
\quad 无。

3.信号量释放

\quad xSemaphoreGive()是一个用于释放信号量的宏,真正的实现过程是调用消息队列通用发送函数。释放的信号量对象必须是已经被创建的,可以用于二值信号量、计数信号量、互斥量的释放,但不能释放由函数xSemaphoreCreateRecursiveMutex()创建的递归互斥量。此外该函数不能在中断中使用。

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );

参数说明:

  • xSemaphore-信号量句柄。

返回值:

  • pdTRUE-信号量被释放。
  • pdFALSE-信号量发生错误,

4.在中断释放信号量

\quad 用于释放一个信号量,带中断保护。被释放的信号量可以是二进制信号量和计数信号量。

BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,signed BaseType_t *pxHigherPriorityTaskWoken)

参数说明:

  • xSemaphore-信号量句柄。
  • pxHigherPriorityTaskWoken-一个或者多个任务有可能阻塞在同一个信号量上,调用函数xSemaphoreGiveFromISR()会唤醒阻塞在该信号量上优先级最高的信号量入队任务,如果被唤醒的任务的优先级大于或者等于被中断的任务的优先级,那么形参
    pxHigherPriorityTaskWoken 就会被设置为
    pdTRUE,然后在中断退出前执行一次上下文切换,中断退出后则直接返回刚刚被唤醒的高优先级的任务。从 FreeRTOS V7.3.0
    版本开始,pxHigherPriorityTaskWoken 是一个可选的参数,可以设置为 NULL。

返回值:

  • pdTRUE-信号量被释放
  • pdFALSE-信号量 发生错误

5.获取一个信号量,可以是二值信号量、计数信号量、互斥量。

\quad xSemaphoreTake()函数用于获取信号量,不带中断保护。获取的信号量对象可以是二 值信号量、计数信号量和互斥量,但是递归互斥量并不能使用这个 API 函数获取。其实获取信号量是一个宏,真正调用的函数是 xQueueGenericReceive ()。该宏不能在中断使用, 而是必须由具体中断保护功能的 xQueueReceiveFromISR()版本代替。

BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait );

参数说明:

  • xSemaphore-信号量句柄。
  • xTicksToWait -等待信号量可用的最大超时时间,单位为 tick(即系统节拍周期)。 如果宏
    INCLUDE_vTaskSuspend 定义为 1 且形参 xTicksToWait 设置为 portMAX_DELAY
    ,则任务将一直阻塞在该信号量上(即没有超时时间)。

返回值:

  • 获取成功则返回pdTRUE,
  • 在指定的超时时间中没有获取成功则返回errQUEUE_EMPTY。

\quad 从该宏定义可以看出释放信号量实际上是一次消息出队操作,阻塞时间由用户指定xTicksToWait,当有任务试图获取信号量的时候,当且仅当信号量有效的时候,任务才能读获取到信号量。如果信号量无效,在用户指定的阻塞超时时间中,该任务将保持阻塞状态以等待信号量有效。当其它任务或中断释放了有效的信号量,该任务将自动由阻塞态转移为就绪态。当任务等待的时间超过了指定的阻塞时间,即使信号量中还是没有可用信号量,任务也会自动从阻塞态转移为就绪态。

6.在中断获取一个信号量,可以是二值信号量、计数信号量

\quad xSemaphoreTakeFromISR()是函数 xSemaphoreTake()的中断版本,用于获取信号量,是一个不带阻塞机制获取信号量的函数,获取对象必须由是已经创建的信号量,信号量类型可以是二值信号量和计数信号量,它与 xSemaphoreTake()函数不同,它不能用于获取互斥xSemaphoreTake()函数不同,它不能用于获取互斥量,因为互斥量不可以在中断中使用,并且互斥量特有的优先级继承机制只能在任务中起作用,而在中断中毫无意义。

BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken)

功能描述:在中断中获一个信号量(其实很少在中断中获取信号量)。可以是二值信号量、计数信号量。

参数说明:

  • xSemaphore-信号量句柄。
  • pxHigherPriorityTaskWoken-一个或者多个任务有可能阻塞在同一个信号量上,调用函数xSemaphoreTakeFromISR()会唤醒阻塞在该信号量上优先级最高的信号量入队任务,如果被唤醒的任务的优先级大于或者等于被中断的任务的优先级,那么形参
    pxHigherPriorityTaskWoken 就会被设置为
    pdTRUE,然后在中断退出前执行一次上下文切换,中断退出后则直接返回刚刚被唤醒的高优先级的任务,可提高实时性。从 FreeRTOS
    V7.3.0 版本开始,pxHigherPriorityTaskWoken 是一个可选的参数,可以设置为 NULL。

返回值:

  • 获取成功则返回 pdTRUE,
  • 没有获取成功则返回 errQUEUE_EMPTY,没有获取成功是因为信号量不可用。

7.创建一个二值信号量

\quad 二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号另更适合用于同步(任务与任务或任务与中断的同步,即控制任务的执行),而互斥信号量适合用于简单的互斥访问。

SemaphoreHandle_t xSemaphoreCreateBinary( void );

参数说明:
\quad

返回值:

  • 如果创建成功则返回一个二值信号量句柄,用于访问创建的二值信号量。
  • 如果创建不成功则返回 NULL。

四、示例代码

1、计数型信号量

#include "semphr.h"

SemaphoreHandle_t  g_sem_count;

int main(void)
{
 	/* 创建计数型的信号量 ,计数值递增到最大值就不能再增加了,初值为0*/
	g_sem_count=xSemaphoreCreateCounting(255,0);   
     
}

void app_task1(void *pvParameters)
{

	while(1)
	{	
		/* 发送信号 */
		xSemaphoreGive(g_sem_count);
		xSemaphoreGive(g_sem_count);
		xSemaphoreGive(g_sem_count);
		
		printf("[app_task1] running ...........\r\n");
		vTaskDelay(1000);
	}
}

void app_task2(void *pvParameters)
{
	
	while(1)
	{	
		/* 等待信号量,portMAX_DELAY一直阻塞等待 */
		xSemaphoreTake(g_sem_count,portMAX_DELAY);
		printf("[app_task2] running ...........\r\n");
	}
}

现象:
在这里插入图片描述

2、二值信号量-保护共享资源

前提:使用二值信号量的任务优先级相同,若不相同可能会导致优先级翻转。

#include "semphr.h"

SemaphoreHandle_t  g_sem_binary;

int main(void)
{
	/* 创建二值型信号量*/
	vSemaphoreCreateBinary(g_sem_binary);
}

void app_task1(void *pvParameters)
{
	while(1)
	{	
		/* 等待信号量,portMAX_DELAY一直阻塞等待 */
		xSemaphoreTake(g_sem_binary,portMAX_DELAY);
		
		printf("[app_task1] running ...........\r\n");
		
		/* 释放信号 */
		xSemaphoreGive(g_sem_binary);
		
		vTaskDelay(1000);
	}
}

void app_task2(void *pvParameters)
{
	while(1)
	{	
		/* 等待信号量,portMAX_DELAY一直阻塞等待 */
		xSemaphoreTake(g_sem_binary,portMAX_DELAY);

		printf("[app_task2] running ...........\r\n");
		
		/* 释放信号 */
		xSemaphoreGive(g_sem_binary);
		
		vTaskDelay(1000);
	}
}

现象:
在这里插入图片描述

五、优先级翻转

1、概述

\quad 优先级翻转是当一个高优先级任务通过信号量机制访问共享资源时,该信号量已被一低优先级任务占有,因此造成高优先级任务被许多具有较低优先级任务阻塞,实时性难以得到保证。

\quad 高优先级任务无法运行而低优先级任务(任务M、任务L)可以运行的现象称为“优先级翻转”。
在这里插入图片描述
(1)任务H和任务M处于挂起状态,等待某一事件的发生,任务L正在运行。
(2)某一时刻任务L想要访问共享资源,在此之前它必须先获得对应该资源的信号量。
(3)任务L获得信号量并开始使用该共享资源。
(4)由于任务H优先级高,它等待的事件发生后便剥夺了任务L的CPU使用权。
(5)任务H开始运行。
(6)任务H运行过程中也要使用任务L正在使用着的资源,由于该资源的信号量还被任务L占用着,任务H只能进入阻塞等待状态,等待任务L释放该信号量。
(7)任务L继续运行。
(8)由于任务M的优先级高于任务L,当任务M等待的事件发生后,任务M剥夺了任务L的CPU使用权。
(9)任务M处理该处理的事。
(10)任务M执行完毕后,将CPU使用权归还给任务L。
(11)最终任务L完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高优先级的任务在等待这个信号量,故内核做任务切换。

\quad 在这种情况下,任务H的优先级实际上降到了任务L的优先级水平。因为任务H要一直等待直到任务L释放其占用的那个共享资源。由于任务M剥夺了任务L的CPU使用权,使得任务H的情况更加恶化,这样就相当于任务M的优先级高于任务H,导致优先级翻转。
\quad 优先级翻转。简单地说,就是高优先级任务必须等待低优先级任务的完成。

示例代码:

static void app_task_Low(void* pvParameters)
{
    uint32_t i;

    while(1)
    {
        xSemaphoreTake(BinarySemaphore,portMAX_DELAY);  	//获取二值信号量
		printf("[taskL]:access res begin\r\n");
        printf("low task Running!\r\n");
		
        for(i=0;  i<0x5000000;  i++)              			//模拟低优先级任务占用二值信号量
        {
            //taskYIELD();                                	//发起任务调度
        }
		printf("[taskL]:access res end\r\n");
        xSemaphoreGive(BinarySemaphore);                	//释放二值信号量
        vTaskDelay(1000);   //延时1s,也就是1000个时钟节拍 
    }	
}   

static void app_task_med(void* pvParameters)
{
	
	EventBits_t EventValue;
	
	for(;;)
	{
		vTaskDelay(1000);   //延时1s,也就是1000个时钟节拍 
        printf("[taskM]:middle task running!\r\n");
	}
} 

static void app_task_high(void* pvParameters)
{
	BaseType_t err=pdFALSE;
	
	for(;;)
	{			
        vTaskDelay(500);    //延时500ms,也就是500个时钟节拍   
		
        xSemaphoreTake(BinarySemaphore,portMAX_DELAY);  //获取二值信号量
		printf("[taskH]:access res begin\r\n");
        printf("high task Running!\r\n");
		printf("[taskH]:access res end\r\n");
        xSemaphoreGive(BinarySemaphore);                //释放信号量
		
        vTaskDelay(500);    //延时500ms,也就是500个时钟节拍  
	}
		
}

在这里插入图片描述

2、解决方案

\quad 为了避免优先级翻转这个问题,RTOS支持一种特殊的二进制信号量:互斥信号量,即互斥锁,用它可以解决优先级翻转问题。
\quad 目前解决优先级翻转有许多种方法。其中普遍使用的有2种方法:一种被称作优先级继承(priority inheritance);另一种被称作优先级天花板(priority ceilings)。
A. 优先级继承(priority inheritance) :优先级继承是指将低优先级任务的优先级提升到等待它所占有的资源的最高优先级任务的优先级。当高优先级任务由于等待资源而被阻塞时,此时资源的拥有者的优先级将会临时自动被提升,以使该任务不被其他任务所打断,从而能尽快的使用完共享资源并释放,再恢复该任务原来的优先级别。

B. 优先级天花板(priority ceilings): 优先级天花板是指将申请某资源的任务的优先级提升到可能访问该资源的所有任务中最高优先级任务的优先级。(这个优先级称为该资源的优先级天花板) 。这种方法简单易行, 不必进行复杂的判断, 不管任务是否阻塞了高优先级任务的运行, 只要任务访问共享资源都会提升任务的优先级。

A和B的区别:优先级继承,只有当占有资源的低优先级的任务被阻塞时,才会提高占有资源任务的优先级;而优先级天花板,不论是否发生阻塞,都提升。

在UCOSIII/FreeRTOS,默认使用方案A,详细如下图。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【生物服务器】数据分析//论文润色/组学技术服务 、表观组分析、互作组分析、遗传转化实验、生物医学

DNA亲和纯化测序&#xff08;DAP-seq&#xff09;和组蛋白甲基化修饰是表观遗传学研究中两个重要的技术手段&#xff0c;它们在揭示基因表达调控机制和染色质结构动态变化中发挥着关键作用。然而&#xff0c;在实践过程中&#xff0c;这两种技术也存在一些痛点和挑战。 DNA亲和…

丹摩征文活动| 摩智云端深度解析:Faster R-CNN模型的训练与测试实战指南

目录 丹摩简介 文章前言Faster R-CNN的简介Faster RCNN的训练与测试提前准备1.1 mobaxterm&#xff08;远程连接服务器&#xff09;1.2 本文的源码下载 目标检测模型 Faster-Rcnn2.1云服务器平台 数据上传内置JupyterLab的使用本地连接使用DAMODEL实例获取实例的SSH访问信息通过…

二叉搜索树介绍

⼆叉搜索树 二叉搜索树的概念二叉搜索树的性能分析查找性能插入性能删除性能 二叉搜索树的插入二叉搜索树的查找二叉搜索树的删除⼆叉搜索树的实现代码测试代码 二叉搜索树key和key/value使⽤场景key搜索场景key/value搜索场景key/value⼆叉搜索树代码实现测试代码 二叉搜索树的…

7.揭秘C语言输入输出内幕:printf与scanf的深度剖析

揭秘C语言输入输出内幕&#xff1a;printf与scanf的深度剖析 C语言往期系列文章目录 往期回顾&#xff1a; VS 2022 社区版C语言的安装教程&#xff0c;不要再卡在下载0B/s啦C语言入门&#xff1a;解锁基础概念&#xff0c;动手实现首个C程序C语言概念之旅&#xff1a;解锁关…

5.4.2-1 编写Java程序在HDFS上创建文件

本次实战涉及使用Java操作Hadoop HDFS&#xff0c;包括创建文件、判断文件存在性及异常处理。通过手动添加依赖、启动HDFS服务&#xff0c;成功在HDFS上创建和检查文件。进一步探索了文件操作的最佳实践&#xff0c;如检查文件存在性以避免重复创建&#xff0c;以及处理HDFS安全…

RabbitMQ教程:路由(Routing)(四)

文章目录 RabbitMQ教程&#xff1a;路由&#xff08;Routing&#xff09;&#xff08;四&#xff09;一、引言二、基本概念2.1 路由与绑定2.2 Direct交换机2.3 多绑定2.4 发送日志2.5 订阅 三、整合代码3.1 EmitLogDirectApp.cs3.2 ReceiveLogsDirectApp.cs3.3 推送所有和接收e…

智云-一个抓取web流量的轻量级蜜罐v1.5

智云-一个抓取web流量的轻量级蜜罐v1.5 github地址 https://github.com/xiaoxiaoranxxx/POT-ZHIYUN 新增功能-自定义漏洞信息 可通过正则来添加相关路由以及响应来伪造 nacos的版本响应如下 日流量态势 月流量态势 抓取流量效果

21.UE5游戏存档,读档,函数库

2-23 游戏存档、读档、函数库_哔哩哔哩_bilibili 目录 1.存档蓝图 2.函数库 2.1保存存档 2.2读取存档&#xff1a; 3.加载游戏&#xff0c;保存游戏 3.1游戏实例对象 3.2 加载游戏 3.3保存游戏 这一节的内容较为错综复杂&#xff0c;中间没有运行程序进行阶段性成果的验…

实验5:网络设备发现、管理和维护

实验5&#xff1a;网络设备发现、管理和维护 实验目的及要求&#xff1a; 通过实验&#xff0c;掌握Cisco 路由器和交换机的IOS配置管理。自动从NTP服务器获取时间信息。能够利用TFTP服务器实现路由器和交换机配置文件的备份和恢复。同时验证CDP协议和LLDP协议的网络参数。完…

vue 项目使用 nginx 部署

前言 记录下使用element-admin-template 改造项目踩过的坑及打包部署过程 一、根据权限增加动态路由不生效 原因是Sidebar中路由取的 this.$router.options.routes,需要在计算路由 permission.js 增加如下代码 // generate accessible routes map based on roles const acce…

DataWorks on EMR StarRocks,打造标准湖仓新范式

在大数据领域&#xff0c;数据仓库和实时分析系统扮演着至关重要的角色。DataWorks 基于大数据引擎&#xff0c;为数据仓库/数据湖/湖仓一体等解决方案提供统一的全链路大数据开发治理平台&#xff0c;为用户带来智能化的数据开发和分析体验。而阿里云提供的 EMR Serverless St…

七、利用CSS和多媒体美化页面的习题

题目一&#xff1a; 利用CSS技术&#xff0c;结合表格和列表&#xff0c;制作并美化 “ 翡翠阁 ”页面。运行效果如下 运行效果&#xff1a; 代码 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>翡翠阁</title>&…

游戏引擎学习第15天

视频参考:https://www.bilibili.com/video/BV1mbUBY7E24 关于游戏中文件输入输出&#xff08;IO&#xff09;操作的讨论。主要分为两类&#xff1a; 只读资产的加载 这部分主要涉及游戏中用于展示和运行的只读资源&#xff0c;例如音乐、音效、美术资源&#xff08;如 3D 模型和…

【动手学深度学习Pytorch】2. Softmax回归代码

零实现 导入所需要的包&#xff1a; import torch from IPython import display from d2l import torch as d2l定义数据集参数、模型参数&#xff1a; batch_size 256 # 每次随机读取256张图片 train_iter, test_iter d2l.load_data_fashion_mnist(batch_size) # 将展平每个…

51单片机基础05 实时时钟-思路及代码参考2、3

目录 一、思路二 1、原理图 2、代码 二、思路三 1、原理图 2、代码 一、思路二 所有设定功能相关的操作均在矩阵键盘进行实现&#xff0c;并在定时器中扫描、计数等 1、原理图 2、代码 #include <AT89X52.h> //调用51单片机的头文件 //------------------…

Notepad++的完美替代

由于Notepad的作者曾发表过可能在开发者代码中植入恶意软件的言论&#xff0c;他备受指责。在此&#xff0c;我向大家推荐一个Notepad的完美替代品——NotepadNext和Notepad--。 1、NotepadNext NotepadNext的特点&#xff1a; 1、跨平台兼容性 NotepadNext基于Electron或Qt…

Python | Leetcode Python题解之第564题数组嵌套

题目&#xff1a; 题解&#xff1a; class Solution:def arrayNesting(self, nums: List[int]) -> int:ans, n 0, len(nums)for i in range(n):cnt 0while nums[i] < n:num nums[i]nums[i] ni numcnt 1ans max(ans, cnt)return ans

面试经典 150 题:20、2、228、122

20. 有效的括号 参考代码 #include <stack>class Solution { public:bool isValid(string s) {if(s.size() < 2){ //特判&#xff1a;空字符串和一个字符的情况return false;}bool flag true;stack<char> st; //栈for(int i0; i<s.size(); i){if(s[i] ( |…

使用vscode+expo+Android夜神模拟器运行react-native项目

1.进入夜神模拟器安装路径下的bin目录 2.输入命令&#xff0c;连接Android Studio 启动夜神模拟器后&#xff0c; 打开安装目录的bin文件夹执行下面的命令&#xff0c;只需执行一次&#xff09; nox_adb.exe connect 127.0.0.1:62001adb connect 127.0.0.1:62001 3.运行项目…

【STM32】USB 简要驱动软件架构图

STM32 USB 软件架构比较复杂&#xff0c;建议去看 UM 1734 或者 st wiki STM32 USB call graph STM32 USB Device Library files organization Reference [1]: https://wiki.stmicroelectronics.cn/stm32mcu/wiki/Introduction_to_USB_with_STM32 [2]: UM1734