FreeRTOS实时操作系统(十)信号量

news2024/12/24 9:11:26

系列文章目录


文章目录

  • 系列文章目录
  • 信号量
    • 二值信号量
      • 二值信号量API函数
        • 创建二值信号量函数
        • 释放二值信号量函数
        • 获取二值信号量函数
      • 实验测试
    • 计数型信号量
      • 计数型信号量API函数
        • 动态创建函数
        • 信号量计数值获取函数
      • 实验测试
    • 优先级反翻转
      • 实验测试
    • 互斥信号量
      • API函数
      • 实验测试


信号量

信号量解决同步问题的机制,可以对共享资源的有序访问。

在这里插入图片描述
当计数值大于0,代表有信号量资源
当释放信号量,计数值即资源数加1
当获取信号量,信号量计数值减1

一般计数值的最大值有限制,最大值为1:二值信号量;最大值不为1:计数型信号量。

在这里插入图片描述

二值信号量

二值信号量的本质是一个队列长度为 1 的队列 ,该队列就只有空和满两种情况。

二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,二值信号量更适合用于同步!

在这里插入图片描述

二值信号量API函数

创建二值信号量—释放二值信号量—获取二值信号量

函数描述
xSemaphoreCreateBinary()使用动态方式创建二值信号量
xSemaphoreCreateBinaryStatic()使用静态方式创建二值信号量
xSemaphoreGive()释放信号量
xSemaphoreGiveFromISR()在中断中释放信号量
xSemaphoreTake()获取信号量
xSemaphoreTakeFromISR()在中断中获取信号量

创建二值信号量函数

#define   xSemaphoreCreateBinary()   \		
xQueueGenericCreate( 1,semSEMAPHORE_QUEUE_ITEM_LENGTH,queueQUEUE_TYPE_BINARY_SEMAPHORE)
#define  semSEMAPHORE_QUEUE_ITEM_LENGTH      ((uint8_t)0U)

#define queueQUEUE_TYPE_BASE                  			( ( uint8_t ) 0U )	/* 队列 */
#define queueQUEUE_TYPE_SET                  			( ( uint8_t ) 0U )	/* 队列集 */
#define queueQUEUE_TYPE_MUTEX                 			( ( uint8_t ) 1U )	/* 互斥信号量 */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE    	( ( uint8_t ) 2U )	/* 计数型信号量 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE     	( ( uint8_t ) 3U )	/* 二值信号量 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX       		( ( uint8_t ) 4U )	/* 递归互斥信号量 

和队列通用的创建函数,参数:队列的长度(1),队列项的大小(0),类型
函数的返回值:NULL:创建失败;其他值:创建成功,返回二值信号量的句柄。

释放二值信号量函数

#define   xSemaphoreGive (  xSemaphore  )    \						
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore )  ,   NULL  ,   semGIVE_BLOCK_TIME  ,   queueSEND_TO_BACK )
#define   semGIVE_BLOCK_TIME                  ( ( TickType_t ) 0U 

形参:
xSemaphore:要释放的信号量句柄(释放是没有阻塞时间的,只有立刻释放或释放失败)

返回值:
pdPASS:释放信号量成功;errQUEUE_FULL:释放信号量失败;

获取二值信号量函数

BaseType_t   xSemaphoreTake( xSemaphore, xBlockTime ) 

形参:
xSemaphore:要获取的任务量句柄
xBlockTime:阻塞时间(获取有阻塞时间,可以永远阻塞)

返回值:
pdTRUE:互殴去信号量成功;
pdFALSE:超时,获取信号量失败。

实验测试

#include "semphr.h"
int fputc(int ch,FILE *f)
{
	HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
	return ch;
}

TaskHandle_t    task1_handler;
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         2
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

QueueHandle_t semphore_handle;

void vTaskCode( void * pvParameters )
 {	 
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
 }
 // Function that creates a task.
 void vOtherFunction( void )
 {
	 
	semphore_handle=xSemaphoreCreateBinary();
    if(semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
	xTaskCreate( vTaskCode, "tak1", 128, NULL, 1, &task1_handler );
	vTaskStartScheduler();
 }

void task1( void * pvParameters )
{
	BaseType_t err;
	if(semphore_handle != NULL)
	{
		err = xSemaphoreGive(semphore_handle);
		if(err == pdPASS)
		{
			printf("信号量释放成功!!\r\n");
		}else printf("信号量释放失败!!\r\n");
	}
    while(1)
    {
		vTaskDelay(100);
    }
}
void task2( void * pvParameters )
{
	    BaseType_t err;
    while(1)
    { 
	 err = xSemaphoreTake(semphore_handle,portMAX_DELAY); /* 获取信号量并死等 portMAX_DELAY可以变换	#define portMAX_DELAY ( TickType_t ) 0xffffffffUL*/
	if(err == pdTRUE)
	{
		printf("获取信号量成功\r\n");
	}		
      vTaskDelay(100);
    }
}

在这里插入图片描述

计数型信号量

计数型信号量相当于队列长度大于1 的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的

事件计数:当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1) ,这种场合一般在创建时将初始计数值设置为 0 。

资源管理:信号量表示有效的资源数目。任务必须先获取信号量(信号量计数值-1 )才能获取资源控制权。当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目

计数型信号量API函数

函数描述
xSemaphoreCreateCounting()使用动态方法创建计数型信号量。
xSemaphoreCreateCountingStatic()使用静态方法创建计数型信号量
uxSemaphoreGetCount()获取信号量的计数值

动态创建函数

#define 	xSemaphoreCreateCounting(  uxMaxCount  ,  uxInitialCount  )   xQueueCreateCountingSemaphore( (  uxMaxCount  ) , (  uxInitialCount  ) ) 

形参:
uxMaxCount :计数值的最大值限定;
uxInitialCount :计数值的初始值;

返回值:
NULL:创建失败;
其他值:创建成功返回计数型信号的句柄;

信号量计数值获取函数

#define 	uxSemaphoreGetCount( xSemaphore ) 	uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )

形参:
xSemaphore:信号量句柄

返回值:
整数:当前信号量的计数值大小

实验测试

QueueHandle_t count_semphore_handle;

 // Function that creates a task.
 void vOtherFunction( void )
 {
	 
	count_semphore_handle = xSemaphoreCreateCounting(100 , 100);  /* 创建计数型信号量,且是满的 */
    if(count_semphore_handle != NULL)
    {
        printf("计数型信号量创建成功!!!\r\n");
    }
	xTaskCreate( vTaskCode, "tak1", 128, NULL, 1, &task1_handler );
	vTaskStartScheduler();
 }
 

//释放计数型信号量
void task1( void * pvParameters )  //优先级是2
{
    while(1)
    {
		xSemaphoreGive(count_semphore_handle);      /* 释放信号量 */
		vTaskDelay(1000);
		printf("信号量释放成功!!\r\n");
    }
}

//获取计数型信号量
void task2( void * pvParameters ) //优先级是3
{
   BaseType_t err = 0;
    while(1)
    {
        err = xSemaphoreTake(count_semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("信号量的计数值为:%d\r\n",(int)uxSemaphoreGetCount(count_semphore_handle));
        }
        vTaskDelay(500);
    }
}

在这里插入图片描述

优先级反翻转

优先级翻转:高优先级的任务反而慢执行,低优先级的任务反而优先执行。

优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。

在使用二值信号量的时候,经常会遇到优先级翻转的问题。

在这里插入图片描述
高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中优先级的任务比高优先级任务具有更高的优先权(即优先级翻转)

实验测试

TaskHandle_t    task1_handler;

#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

/* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128
TaskHandle_t    high_task_handler;
void high_task( void * pvParameters );


void vTaskCode( void * pvParameters )
 {	 
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
				
	 xTaskCreate((TaskFunction_t         )   high_task,
                (char *                 )   "high_task",
                (configSTACK_DEPTH_TYPE )   TASK3_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK3_PRIO,
                (TaskHandle_t *         )   &high_task_handler );
				
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
 }
 
QueueHandle_t semphore_handle;

 // Function that creates a task.
 void vOtherFunction( void )
 {
	 
	semphore_handle = xSemaphoreCreateBinary();
    if(semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
    xSemaphoreGive(semphore_handle);        /* 释放一次信号量 */
	
	xTaskCreate( vTaskCode, "tak1", 128, NULL, 1, &task1_handler );
	vTaskStartScheduler();
 }
 

//低优先级任务
void task1( void * pvParameters )
{
    while(1) 
    {
        printf("低优先级任务获取信号量\r\n");
        xSemaphoreTake(semphore_handle,portMAX_DELAY);
        printf("低优先级任务正在运行!!!\r\n");
        HAL_Delay(3000);
        printf("低优先级任务释放信号量\r\n");
        xSemaphoreGive(semphore_handle); 
        vTaskDelay(1000);
    }
}

//中等优先级任务
void task2( void * pvParameters )
{
    while(1)
    {
        printf("中优先级任务正在运行!!!\r\n");
        vTaskDelay(1000);
    }
}

/* 任务三,高优先级任务 */
void high_task( void * pvParameters )
{
    while(1)
    {
        printf("高优先级任务获取信号量\r\n");
        xSemaphoreTake(semphore_handle,portMAX_DELAY);
        printf("高优先级任务正在运行!!!\r\n");
        HAL_Delay(1000);
        printf("高优先级任务释放信号量\r\n");
        xSemaphoreGive(semphore_handle); 
        vTaskDelay(1000);
    }
}

在这一段,低优先级占用二值信号量导致高优先级任务进入阻塞,一直是其他优先级任务执行。
在这里插入图片描述

互斥信号量

互斥信号量是一个拥有优先级继承的二值信号量,在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中!

优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

在这里插入图片描述
任务H的阻塞时间仅仅是任务L 的执行时间,将优先级翻转的危害降到了最低

优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响
注意:互斥信号量不能用于中断服务函数中,原因如下:
(1) 互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级, 所以互斥信号量只能用与任务中,不能用于中断服务函数。
(2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

API函数

宏configUSE_MUTEXES置1
使用流程:创建互斥信号量,获取信号量 ,释放信号量

创建函数:

函数描述
xSemaphoreCreateMutex()使用动态方法创建互斥信号量。
xSemaphoreCreateMutexStatic()使用静态方法创建互斥信号量。

创建互斥信号量时,会主动释放一次信号量。

#define   xSemaphoreCreateMutex()      xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

返回值:
NULL:创建失败
其他值:创建成功返回互斥信号量的句柄

实验测试

QueueHandle_t mutex_semphore_handle;

 // Function that creates a task.
 void vOtherFunction( void )
 {
	mutex_semphore_handle = xSemaphoreCreateMutex();
    if(mutex_semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
	xTaskCreate( vTaskCode, "tak1", 128, NULL, 1, &task1_handler );
	vTaskStartScheduler();
 }
 

//低优先级任务
void task1( void * pvParameters )
{
    while(1) 
    {
        printf("低优先级任务获取信号量\r\n");
        xSemaphoreTake(mutex_semphore_handle,portMAX_DELAY);
        printf("低优先级任务正在运行!!!\r\n");
        HAL_Delay(3000);
        printf("低优先级任务释放信号量\r\n");
        xSemaphoreGive(mutex_semphore_handle); 
        vTaskDelay(1000);
    }
}

//中等优先级任务
void task2( void * pvParameters )
{
    while(1)
    {
        printf("中优先级任务正在运行!!!\r\n");
        vTaskDelay(1000);
    }
}

/* 任务三,高优先级任务 */
void high_task( void * pvParameters )
{
    while(1)
    {
        printf("高优先级任务获取信号量\r\n");
        xSemaphoreTake(mutex_semphore_handle,portMAX_DELAY);
        printf("高优先级任务正在运行!!!\r\n");
        HAL_Delay(1000);
        printf("高优先级任务释放信号量\r\n");
        xSemaphoreGive(mutex_semphore_handle); 
        vTaskDelay(1000);
    }
}

在这里插入图片描述

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

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

相关文章

深兰科技与韩国EVERYBOT集团签署服务机器人出口订单

7月4日,在深兰科技集团上海总部,韩国EVERYBOT Inc.集团与深兰科技智胜(上海)科技有限公司签署了服务机器人出口订单及韩国市场战略合作协议。根据协议,EVERYBOT将从深兰科技订购首批服务机器人,同时成为深兰科技各类服务机器人产品…

Layui如何给lay-data插入按钮呢?如何通过按钮获取Id值呢?

😇作者介绍:一个有梦想、有理想、有目标的,且渴望能够学有所成的追梦人。 🎆学习格言:不读书的人,思想就会停止。——狄德罗 ⛪️个人主页:进入博主主页 🗼推荐系列:点击进入 &#…

机器学习实战:Python基于NN神经网络进行分类(十一)

文章目录 1 前言1.1 神经网络的介绍1.2 神经网络的应用 2. Tensorflow实战演示2.1 导入函数2.2 导入数据2.3 数据预处理2.4 建立神经网络2.5 训练模型2.6 评估模型2.7 预测 3. 讨论 1 前言 神经网络(Neural network,NN)机器学习是一种基于人…

【动态规划算法】第五题:62.不同路径

💖作者:小树苗渴望变成参天大树 🎉作者宣言:认真写好每一篇博客 🎊作者gitee:gitee 💞作者专栏:C语言,数据结构初阶,Linux,C 动态规划算法 如 果 你 喜 欢 作 者 的 文 章 ,就 给 作…

查看虚拟机主机IP

虚拟机主机ip 文章目录 ifconfigip addr图形化界面 ifconfig 失败了 ip addr 图形化界面

动手学DL——环境部署随笔【深度学习】【Anaconda】【CUDA】【PyTorch】【jupyter】

文章目录 1、环境部署1.1、安装 Anaconda1.2、安装 GPU版 PyTorch1.3、安装结束1.4、 jupyter 连接虚拟环境 1、环境部署 记录虚拟环境安装部署细节,以备重装。 1.1、安装 Anaconda anaconda 2022.10 windows 版本,https://repo.anaconda.com/archive/ 加入环境变量…

如何在 Django框架下完成 websocket 连接 在 Heroku 上部署 websocket 应用

文章目录 websocket 和 socket 连接的区别与 Django 建立 websocket 连接ASGI v.s. WSGIDjango > 3.0ChannelsDaphnesettings.pyconsumers.py & routing.py 测试 websocket 连接postman Heroku 部署 websocket 应用asgi.pyProcfile websocket 和 socket 连接的区别 Webs…

TLS、SSL、CA 证书、公钥、私钥

1. HTTP 的问题 HTTP 协议是超文本传输协议(Hyper Text Transfer Protocol)的缩写,它是从 WEB 服务器传输超文本标记语言 HTML 到本地浏览器的传送协议。HTTP 设计之初是为了提供一种发布和接收 HTML 页面的方法,时至今日&#x…

计算机组成原理(期末或考研备考)- 主存储器,DRAM,SRAM,ROM

讲解视频 SRAM VS DRAM DRAM工作原理 DRAM采用栅极电容上的电荷存储信息,由于DRAM上的电容电荷一般只能维持1-2ms,即使电源不断电,信息也会自动消失。因此每隔一定时间必须刷新。 集中刷新,利用固定的时间对所有的行进行刷新&am…

程序员和网络安全的优劣势一览表

程序员的优点: 学的都是计算机基础和一些程序语言,入门比较简单,无论什么行业参加几个月的培训找到一份月薪5K的工作还是没有问题的,人才的需求量虽然没有之前多,但是对真正有技术的人才需求还是挺大的。 程序员的缺…

Python_魔法属性和方法

目录 魔法属性 __doc__ __moudle__ __class__ __name__ __dict__ 魔法方法 __new__() __init__ () __del__() __call__() __str__ () __getitem__(),__setitem__(),__delitem__() __setattr__()、__getattr__() __iter__() …

构建LLM应用你所要知道的事情;Midjourney可以生成图片外的场景

🦉 AI新闻 🚀 AI作画工具Midjourney推出新功能"Pan",可以生成图片外的场景 摘要:AI作画工具Midjourney最近推出了"pan"功能,用户可以通过控制输入框中的"上下左右"来平移图片以生成场…

【案例】VR全景图:效果+源码

狠人话不多说,直接放视频效果地址 一、效果 1.视频效果 视频效果地址:点击这里 2.图片效果 二、构思 该怎么实现?页面如何布局页面是否可随意控制显示1.功能 控制页面显示数量可放大控制全景图+自动播放左右按钮控制上一页或下一页(尾页:下一页按钮隐藏,首页:上一页按…

Spring Boot 中的熔断器:原理和使用

Spring Boot 中的熔断器:原理和使用 什么是熔断器? 熔断器是一种用于处理分布式系统中故障的设计模式。它可以防止出现故障的服务对整个系统造成连锁反应。熔断器通过监控故障服务的调用情况,当故障服务出现问题时,熔断器会迅速…

【vue】Vue3中使用函数调用组件内函数和创建组件【超详细】

uniappuview vue3typescript版本,使用函数方式调用和创建组件,并使用组件内的方法 项目场景 今天突然觉得在视图上应用组件,然后在script脚本里操作组件这方式特别的麻烦。因为每次使用组件时都要进行应用,不管你用不用你都要引…

TDengine“露面”中国油气田企业智慧油田技术交流大会,为时序数据处理带来全新思路

2023 年 7 月 4 日- 6 日,由中国石油和化学工业联合会主办,中国石油油气和新能源分公司、中国石化油田勘探开发事业部、中国海洋石油有限公司勘探开发部协办的“中国油气田企业智慧油田技术交流大会”在北京市召开。本次大会邀请了中国石油、中国石化、中…

Android Studio实现内容丰富的安卓房屋出租租赁平台

如需源码可以添加q-------3290510686,也有演示视频演示具体功能,源码不免费,尊重创作,尊重劳动。 项目编号063 1.开发环境 android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端: 1.注册登录 2.查看租房列表…

SSMP整合案例(12) 在界面中实现删除操作

接下来我们来说删除 首先 我们要在表格上加上删除和修改两个操作按钮 我们先在App.vue页面部分编写 参考代码如下 <el-table-columnalign"right"label"操作" ><template slot-scope"scope"><el-buttonsize"mini"type…

王道考研数据结构——基本概念

06 算法的时间复杂度 线性的时间复杂度 O(n^3)O(n^2*logn) O(n^3) O(logN) 无法确定&#xff0c;和输入的数据量有关系&#xff0c;使用平均复杂度&#xff1a; 最坏/平均时间复杂度 07 算法的空间复杂度 S(n)n S(n)O(n^2) 刷题&#xff1a; O(n^3) 答案&#xff1a; B…

Maven 配置本地jar,通过下载第三方jar包,然后手动配置maven jar包依赖 例如:IKExpression

说明&#xff1a;有时候有一些jar包 maven中央仓库和阿里云仓库没有收录的jar包需要手动下载至本地进行手动添加maven依赖&#xff0c;就拿 IK表达式 IKExpression jar 包来说 第一步 下载IKExpression 包 没有这个包的同学可以点击下载阿里云盘分享 第二步 找到自己项目本地…