互斥量 的初识

news2024/9/24 7:21:07

Q: 什么是互斥量?

A: 在多数情况下,互斥型信号量和二值型信号量非常相似,但是从功能上二值型信号量用于同步, 而互斥型信号量用于资源保护。 互斥型信号量和二值型信号量还有一个最大的区别,互斥型信号量可以有效解决优先级反转现象

优先级反转现象

以上图为例,系统中有3个不同优先级的任务 H/M/L,最高优先级任务H和最低优先级任务L通过普通二值信号量机制,共享资源。目前任务L占有资源,锁定了信号量,Task H运行后将被阻塞,直到Task L释放信号量后,Task H才能够退出阻塞状态继续运行。但是Task H在等待Task L释放信号量的过 程中,中等优先级任务M抢占了任务L(能够抢占的原因是Task H 和 L 通过信号量来共享资源,但是Task M没有参考信号量,所以可以直接打断优先级低的任务),从而延迟了信号量的释放时间,导致Task H阻塞了更长时间,Task M优先级明明比Task H低,但是却可以阻塞它,这种现象称为优先级倒置或反转

优先级继承

优先级继承是一种使用互斥信号量解决“优先级反转”问题的方法,但是不能完全解决,只能尽可能降低“优先级反转”带来的影响。

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

如果上图中的例子中,Tas H 和 Task L 共享的不是普通二值信号量而是互斥信号量,那么当Task L占用信号量的时候,高优先级的Task H依然会被阻塞,但是同时也会将低优先级的Task L的优先级提升至和自己一样的最高级所以中等优先级的Task M就无法再抢占Task L了,Task H也就不会阻塞很长时间了

互斥量相关 API 函数

注意!互斥信号量不能用于中断服务函数中!

  • 输入参数:无
  • 返回值: 成功,返回对应互斥量的句柄; 失败,返回 NULL 

注意,和之前普通二值信号量和计数信号量不同,之前的二值和计数信号量的原厂API函数在创建完信号量之后就结束了,是Cube再次对其封装,赋予了“创建完信号量就全部释放”的功能,但是互斥量的原厂API函数在创建后,就会自动释放一个互斥量,这个功能不需要Cube封装,是自带的

 

实操演示

需求: 1. 演示优先级反转  2. 使用互斥量优化优先级翻转问题

在 C:\mjm_CubeMX_proj 路径下,复制一份Cube的母版并重命名为 :mjm_freeRTOS_Mute:

优先级反转演示:

1. 打开相应的Cube文件,找到左侧的Middleware --> FREERTOS

1.1 然后在下方找到"Task and Queues",创建三个不同优先级的任务:

 

1.2 在下方找到"Timers and Semaphores",创建普通二值信号量:

2. 生成代码并打开Keil, 编写三个任务的内容:

注意!在写释放信号量的代码时,不能写成下面这种形式,因为在这个例程中,TASK_L和TASK_H都在不停的获取信号量,一旦将printf写在释放信号量函数之后,就会导致printf显示的位置错误,因为信号量一旦被释放,就会被另一个任务所获取了

if (xSemaphoreGive(myBinarySemHandle) == pdTRUE){ //释放信号量并判断返回值
    printf("Task_L has stopped\r\n");
}
#include "stdio.h"

void StartTask_H(void const * argument)
{
  for(;;)
  {
    if (xSemaphoreTake(myBinarySemHandle, portMAX_DELAY ) == pdTRUE){ //获取信号量并判断返回值,注意阻塞时间要设为最大,否则当TASK_L占用时,其他任务会直接获取失败并返回,不会等了
			printf("Task_H is running......\r\n");
			HAL_Delay(3000);
			printf("Task_H has stopped\r\n");
			xSemaphoreGive(myBinarySemHandle); //释放信号量
		}
		osDelay(1000); //注意,此处的Delay必不可少,因为如果没有Delay,信号量将不断被TASK_H 和 Task_L 所获取和释放,轮不到Task_M执行,无法复现优先级反转的效果
  }
}

void StartTask_M(void const * argument)
{
  for(;;)
  {
		printf("Task_M:HELLO MJM\r\n"); //Task_M不占用信号量,只是单纯占用CPU打印一句话
    osDelay(1000);
  }
}

void StartTask_L(void const * argument)
{
  for(;;)
  {
    if (xSemaphoreTake(myBinarySemHandle, portMAX_DELAY ) == pdTRUE){ //获取信号量并判断返回值,注意阻塞时间要设为最大,否则当TASK_L占用时,其他任务会直接获取失败并返回,不会等了
			printf("Task_L is running......\r\n");
			HAL_Delay(3000);
			printf("Task_L has stopped\r\n");
			xSemaphoreGive(myBinarySemHandle); //释放信号量
		}
		osDelay(1000); //注意,此处的Delay必不可少,因为如果没有Delay,信号量将不断被TASK_H 和 Task_L 所获取和释放,轮不到Task_M执行,无法复现优先级反转的效果
  }
}

 

实现效果1

打开串口助手:

 回顾刚刚提到的优先级反转的例子:

可见,被鼠标蓝色选中的区域就发生了优先级反转的现象

 

使用互斥量优化的演示:

1. 在上个演示的Cube文件中,找到左侧的Middleware --> FREERTOS

1.1 在下方找到"Timers and Semaphores",删除刚刚创建的普通二值信号量:

1.2 在下方找到"Mutexes",创建互斥量:

2. 生成代码并打开Keil, 重写三个任务的内容:

2.1 在freertos.c 中可以看到创建互斥量的代码,和二值信号量的创建非常类似 

2.2 重写代码,其实就是将二值信号量的句柄换成互斥量:

使用二值信号量的获取和释放函数可以直接适用于互斥量,从侧面印证了互斥量就是一种特殊的二值信号量

#include "stdio.h"

void StartTask_H(void const * argument)
{
  for(;;)
  {
    if (xSemaphoreTake(myMutexHandle, portMAX_DELAY ) == pdTRUE){ //获取信号量并判断返回值,注意阻塞时间要设为最大
			printf("Task_H is running......\r\n");
			HAL_Delay(3000);
			printf("Task_H has stopped\r\n");
			xSemaphoreGive(myMutexHandle); //释放信号量
		}
		osDelay(1000); //注意,此处的Delay必不可少,因为如果没有Delay,信号量将不断被TASK_H 和 Task_L 所获取和释放,轮不到Task_M执行,无法复现优先级反转的效果
  }
}


void StartTask_M(void const * argument)
{
  for(;;)
  {
		printf("Task_M:HELLO MJM\r\n"); //Task_M不占用信号量,只是单纯占用CPU打印一句话
    osDelay(1000);
  }
}


void StartTask_L(void const * argument)
{

  for(;;)
  {
    if (xSemaphoreTake(myMutexHandle, portMAX_DELAY ) == pdTRUE){ //获取信号量并判断返回值,注意阻塞时间要设为最大,否则当TASK_L占用时,其他任务会直接获取失败并返回,不会等了
			printf("Task_L is running......\r\n");
			HAL_Delay(3000);
			printf("Task_L has stopped\r\n");
			xSemaphoreGive(myMutexHandle); //释放信号量
		}
		osDelay(1000); //注意,此处的Delay必不可少,因为如果没有Delay,信号量将不断被TASK_H 和 Task_L 所获取和释放,轮不到Task_M执行,无法复现优先级反转的效果
  }
}

实现效果2

再次打开串口助手:

可见,在使用互斥量了之后,Task_M不再具备打断Task_L的能力了。 

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

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

相关文章

CAD .NET 15.0 企业版 Crack

CAD .NET 15.0 企业版 企业版 企业版 企业版 企业版 Updated: June 14, 2023 | Version 15.0 NEW CAD .NET is a library for developing solutions in .NET environment. It supports AutoCAD DWG/ DXF, PLT and other CAD formats. The library can be used in a wide rang…

SpringBoot百货超市商城系统 附带详细运行指导视频

文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码 一、项目演示 项目演示地址: 视频地址 二、项目介绍 项目描述:这是一个基于SpringBoot框架开发的百货超市系统。首先,这是一个很适合SpringBoot初学者学习的项目,代…

在linux上面部署activemq

1、下载 网址:ActiveMQ 注意:新版本5.17起 要求jdk11, 5.16兼容jdk8, 所以,确保已经安装 java11 或以上的版本 这里安装较新版:5.18.2,已经安装了java17 如何安装jdk17,请详见我的另一篇文章:linux…

mermaid使用记录

记录mermaid可使用到场景,部分关键使用过程备忘 markdown idea开启mermaid预览 grafana 参考插件 Diagram https://grafana.com/grafana/plugins/jdbranham-diagram-panel/ 结合监控数据,可以展示某个处理流程中,各个中间环节的处理指标及…

巧用NGINX配置解决跨域问题

页面nginx配置 1,前端页面放在域名根目录,比如,http://www.xuecheng.com/ ,对应的nginx配置: #门户location / {alias D:/Z_lhy/SpringCloud/xuecheng_online/www/xc-ui-pc-static-portal/;index index.html;} 页…

VMPWN的入门系列-2

温馨提示: 文章有点长,图片比较多,请耐心阅读 实验四 VMPWN4 题目简介 这道题应该算是虚拟机保护的一个变种,是一个解释器类型的程序,何为解释器?解释器是一种计算机程序,用于解释和执行源代码。…

quarkus核心编程笔记

此篇只做总结,有大佬做的更详细 大佬quarkus笔记 依赖注入 在应用中,一个接口有多个实现是很常见的,那么依赖注入时,如果类型是接口,如何准确选择实现呢? 修饰符匹配Named注解属性匹配根据优先级选择写…

小红书推广 方法总结

大家好,我是网媒智星,今天跟大家分享一下小红书的推广方法和经验。 一、平台简介 1、什么是小红书? 小红书是一个消费决策/生活方式平台,用户可以通过图片、文案、视频等方式分享美好生活。 2、用户画像 - 2亿月活跃…

better scoll的使用以及注意事项以及左联右

下载better scoll的核心 在你要使用的页面引入 在data里面定义一个对象 然后在createad里面放一个nexttick异步操作。 上面是获取这个left-box节点是父节点 记住里面只能有一个子节点如果循环了 就要再包一个div就是一个子节点 左联右 首先也要获取 右边的 父节点 然后配…

RPC与REST有什么区别?

背景 好多开发的同学在工作中,经常分不清RPC和REST的区别,导致经常沟通不在一个层次上。甚至有些同学把这两个当成同一个东西。 RPC与REST的区别? 对比名称rpcrest备注架构风格RPC是基于过程调用的架构风格,它将远程方法调用封装为…

深度学习技巧应用24-深度学习手撕代码与训练流程的联系记忆方法

大家好,我是微学AI,今天给大家介绍一下深度学习技巧应用24-深度学习手撕代码与训练流程的联系记忆方法,大家都知道深度学习模型训练过程是个复杂的过程,这个过程包括数据的收集,数据的处理,模型的搭建,优化器的选择,损失函数的选择,模型训练,模型评估等步骤,其中缺少…

gitee使用参考

Git代码托管服务 2.1 常用的Git代码托管服务 gitHub( 地址:https://github.com/ )是一个面向开源及私有软件项目的托管平台,因为只支持Git 作为唯一的版本库格式进行托管,故名gitHub码云(地址:…

shell脚本:数据库的分库分表

#!/bin/bash ######################### #File name:db_fen.sh #Version:v1.0 #Email:admintest.com #Created time:2023-07-29 09:18:52 #Description: ########################## MySQL连接信息 db_user"root" db_password"RedHat123" db_cmd"-u${…

ROS暑期学校分享-2023

云课的优势 https://gitcode.net/ZhangRelay/cocubesim 网络编程和单机编程 网络编程和单机编程是两种不同的编程方式,它们的主要区别在于其应用场景和实现技术上。 1 应用场景 网络编程主要用于构建基于互联网的应用程序,例如Web应用程序、网上购物…

安装typora

1、下载压缩包 链接:https://pan.baidu.com/s/1nFvk3hAyXNbvKPJnu9ipIA 提取码:sdyy 2、安装typora 3、打开Crack 4、将这个dll文件复制粘贴到typora的安装路径里

Linux--进程的新建状态

新建状态: 操作系统创建了进程的内核数据结构(task_struct、mm_struct、页表),但是页表没有创建映射关系,而且磁盘里的程序的代码和数据未加载到物理内存

Spring注解系列——@PropertySource

在Spring框架中PropertySource注解是非常常用的一个注解,其主要作用是将外部化配置解析成key-value键值对"存入"Spring容器的Environment环境中,以便在Spring应用中可以通过Value或者占位符${key}的形式来使用这些配置。 使用案列 // Propert…

React 路由使用-详细介绍

路由初使用 抽象路由模块 src\page\Article\index.js const Article () > {return (<div><p>文章页</p></div>); };export default Article;src\router\index.js // 导入页面 import Article from "../page/Article"; import Login fr…

Leetcode 剑指 Offer II 037. 小行星碰撞

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个整数数组 asteroids&#xff0c;表示在同一行的小行星。…

无涯教程-jQuery - Dragable拖动函数

Drag-able 函数可与JqueryUI中的交互一起使用。此函数可在任何DOM元素上启用可拖动功能。无涯教程可以通过使用鼠标单击来拖动可拖动元素。 Drag able - 语法 $( "#draggable" ).draggable(); Drag able - 示例 下面是一个简单的示例&#xff0c;显示可拖动的用法…