鸿蒙Hi3861学习七-Huawei LiteOS(信号量)

news2024/11/20 8:47:15

一、简介

        信号量(Semaphore)是一种实现任务间通信的机制,实现任务之间同步临界资源的互斥访问。常用于协助一组相互竞争的任务来访问临界资源。

        在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。

        通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数。其值的含义分为两种情况:

  • 0,表示没有积累下来的Post信号量操作,且有可能有再此信号量上阻塞的任务
  • 正值,表示有一个或多个Post信号量操作。

        以同步为目的的信号量和以互斥为目的的信号量在使用上是有不同的:

  • 同步信号量:信号量在创建后被置为空,任务1取信号量而阻塞,任务2在某种条件发生后,释放信号量,于是任务1得以进入READY或RUNNING态,从而达到两个任务间的同步。
  • 互斥信号量:信号量创建后计数是满的,在需要使用临界资源时,先取信号量,使其变空。这样,其他任务需要使用临界资源时就会因为无法取得信号量而阻塞,从而保证了临界资源的安全。

        更多信号量概念,可参考:FreeRTOS学习五(信号量)_freertos信号量用法_t_guest的博客-CSDN博客

二、运行机制

        信号量初始化为配置的N个信号量申请内存(N值可以由用户自行配置,受内存限制),并把所有的信号量初始化成未使用状态,并加入到未使用链表中供系统使用

        信号量创建,从未使用的信号量链表中获取一个信号量,并设定初值

        信号量申请,若其计数值大于0,则直接减1并返回成功否则任务阻塞,等待其他任务释放该信号量,等待的超时时间可设定。当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾

        信号量释放,若没有任务等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待任务队列上的第一个任务。

        信号量删除,将正在使用的信号量置为未使用的状态,并挂回到未使用链表中。

        信号量允许多个任务在同一时刻访问同一资源,但会限制同一时刻访问此资源的最大任务数目。访问同一资源的任务数达到该资源的最大数量时,会阻塞其他试图获取该资源的任务,直到有任务释放该信号量。

三、API介绍

      osSemaphoreNew

        函数功能:

        创建信号量。不可在中断服务中使用

        函数原型:

osSemaphoreId_t osSemaphoreNew(uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr)

        参数:

        max_count:信号量最大的可用数量

        initial_count:初始化时可用数量

        attr:相关属性,只有在自定义内存才用的到。默认为NULL

        返回值:

        NULL:失败

        其他值:信号量标识符

        实例:

osSemaphoreId_t sem1;
sem1 = osSemaphoreNew(4, 0, NULL);

      osSemaphoreAcquire

        函数功能:

        阻塞任务,等待信号量。如果等待时间为0,可以在中断中调用

        函数原型:

osStatus_t osSemaphoreAcquire(osSemaphoreId_t semaphore_id, uint32_t timeout)

        参数:

        semaphore_id:信号量ID。由osSemaphoreNew信号量创建时获得。

        timeout:等待超时时间。osWaitForever死等

        返回值:

        osOK:等到信号量

        其他值:异常

typedef enum {
  /** Operation completed successfully */
  osOK                      =  0,
  /** Unspecified error */
  osError                   = -1,
  /** Timeout */
  osErrorTimeout            = -2,
  /** Resource error */
  osErrorResource           = -3,
  /** Incorrect parameter */
  osErrorParameter          = -4,
  /** Insufficient memory */
  osErrorNoMemory           = -5,
  /** Service interruption */
  osErrorISR                = -6,
  /** Reserved. It is used to prevent the compiler from optimizing enumerations. */
  osStatusReserved          = 0x7FFFFFFF
} osStatus_t;

        实例:

osSemaphoreId_t sem1;
osStatus_t ret = osSemaphoreAcquire(sem1,osWaitForever);

      osSemaphoreRelease

        函数功能:

        释放信号量。可以在中断中调用

        函数原型:

osStatus_t osSemaphoreRelease(osSemaphoreId_t semaphore_id)

        参数:

        semaphore_id:信号量ID。由osSemaphoreNew信号量创建时获得。

        返回值:

        osOK:成功

        其他值:异常

        实例:

osSemaphoreId_t sem1;
osStatus_t ret = osSemaphoreRelease(sem1);

      osSemaphoreGetCount

        函数功能:

        获取当前可用的信号量数目。可以在中断中被调用

        函数原型:

uint32_t osSemaphoreGetCount(osSemaphoreId_t semaphore_id)

        参数:

        semaphore_id:信号量ID。由osSemaphoreNew信号量创建时获得。

        返回值:

        可用的信号量数

        实例:

osSemaphoreId_t sem1;
osSemaphoreGetCount(sem1)

      osSemaphoreDelete

        函数功能:

        删除信号量。不可在中断中被调用。

        函数原型:

osStatus_t osSemaphoreDelete(osSemaphoreId_t semaphore_id)

        参数:

        semaphore_id:信号量ID。由osSemaphoreNew信号量创建时获得。

        返回值:

        osOK:成功

        其他值:异常

        实例:

osSemaphoreId_t sem1;
osStatus_t ret = osSemaphoreDelete(sem1);

四、实例

        创建一个经典的生产者与消费者模型。其中,生产者一次生产3个,消费之每次消费一个。但是生产者每5秒生产一次。消费者有可消耗就消耗。

#define LOG_I(fmt, args...)   printf("<%8ld> - [TIMER]:"fmt"\r\n",osKernelGetTickCount(),##args);
#define LOG_E(fmt, args...)   printf("<%8ld>-[TIMER_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTickCount(), ##args);

osSemaphoreId_t sem1;

osThreadId_t g_thread1_id = NULL;
osThreadId_t g_thread2_id = NULL;

/*****任务一*****/
void thread1(void)
{
    LOG_I("thread 1 start");

    while (1)
    {
        LOG_I("thread 1 delay 5S");
        osDelay(500);
        LOG_I("thread 1 release semaphore before");
        osSemaphoreRelease(sem1);
        osSemaphoreRelease(sem1);
        osSemaphoreRelease(sem1);
        LOG_I("thread 1 release semaphore after,viable sema count:%d",osSemaphoreGetCount(sem1));
    }

    LOG_I("thread 1 break");
    
    osThreadTerminate(g_thread1_id);
}

/*****任务二*****/
void thread2(void)
{
    LOG_I("thread 2 start");
    
    while (1)
    {
        LOG_I("thread2 acquire semaphore wait,sema count:%d",osSemaphoreGetCount(sem1));
        osSemaphoreAcquire(sem1,osWaitForever);
        LOG_I("thread2 acquire semaphore accepted");
    }
    LOG_I("thread 2 end");
}

void Hello_World(void)
{  

    LOG_I("Test semaphore");

    sem1 = osSemaphoreNew(4, 0, NULL);
    if (sem1 == NULL)
    {
        LOG_E("Falied to create Semaphore1!");
    }
    osThreadAttr_t attr;

    attr.name = "thread1";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 1024 * 2;
    attr.priority = osPriorityNormal; 

    g_thread1_id  = osThreadNew((osThreadFunc_t)thread1, NULL, &attr);
    if (g_thread1_id == NULL)
    {
        LOG_E("Falied to create thread1!");
    }

    attr.name = "thread2";
    attr.stack_size = 1024 * 2;

    g_thread2_id = osThreadNew((osThreadFunc_t)thread2, NULL, &attr);
    if (g_thread2_id == NULL)
    {
        LOG_E("Falied to create thread2!");
    }
}

        看结果:

         可以看到,在任务1里直接释放三个信号量,而此时调用osSemaphoreGetCount只能获取到2个可用。是因为在osSemaphoreRelease释放信号量时,因为任务2已经在等待信号量,所以任务1释放的一个信号量马上被任务2所获取。

        那为什么任务2获取到信号量后并没有马上到任务2执行呢?是因为两个任务的优先级是一样的只有等到任务1释放了系统后,任务2才能开始运行。如果我们把任务2的优先级设置比任务1高结果会如何呢?

        在代码中添加一行代码:

         再看一下运行结果。

         这里可以看到,因为任务2的优先级比任务1的优先级高。所以,当任务1释放信号量后,任务2马上抢占了系统的使用权。任务1因为优先级低,被挂起。当任务2执行完后,任务1才能继续执行。

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

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

相关文章

阿里工作7年,肝到P8就靠这份学习笔记了,已助14个朋友拿到offer

​ 在阿里工作了7年&#xff0c;工作压力大&#xff0c;节奏快&#xff0c;但是从技术上确实得到了成长&#xff0c;尤其是当你维护与大促相关的系统的时候&#xff0c;熬到P8也费了不少心思。 技术的更新迭代越来越快&#xff0c;程序员或许是这个过程中最为挣扎的一波人。每…

第0章 学习之前的准备

突然想写点关于linux的东西&#xff0c;一是将自己几十年来零碎的知识作以串联&#xff0c;二是能为正在学习路上的新手作些指引。而恰好作者的孩子是一位初一的学生&#xff0c;我写的这些东西也正是我手把手教授他的&#xff0c;现在分享出来并且命名为《linux中学教程》&…

记一次SpringBoot应用性能调优过程

背景 使用SpringBoot、MyBatis-Plus开发一个接口转发的能&#xff0c;将第三方接口注册到平台中&#xff0c;由平台对外提供统一的地址&#xff0c;平台转发时记录接口的转发日志信息。开发完成后使用Jmeter进行性能测试&#xff0c;使用100个线程、持续压测180秒&#xff0c;…

Java中池化技术探讨

背景&#xff1a;在日常开发中&#xff0c;除了考虑IO操作、线程上下文切换、GC的影响性能外。还通过池化技术提高性能通过循环复用资源&#xff0c;降低资源创建和销毁带来的开销和损失&#xff0c;从而提高性能&#xff0c;例如对象池、内存池、线程池、连接池 一、对象池&a…

软件测试 - 测试用例设计方法之等价类划分和边界值分析

1. 等价类划分法 1.1 基本理论 等价类划分法是通过科学的方法找到具有共同特性的测试输入的集合&#xff0c;避免进行穷举测试&#xff0c;大大减少了测试用例的数量&#xff0c;从而提高测试效率。等价类划分法的典型应用场景就是输入框&#xff0c;适用于较少数量输入框的场…

晶振概述及工作原理

晶振在电路板中随处可见&#xff0c;只要用到处理器的地方就必定有晶振的存在&#xff0c;即使没有外部晶振&#xff0c;芯片内部也有晶振。 晶振概述 晶振一般指晶体振荡器。晶体振荡器是指从一块石英晶体上按一定方位角切下薄片&#xff08;简称为晶片&#xff09;&#xf…

虚拟服务器基础架构解决方案:用最小的工作量实现最大的价值

虚拟服务器基础架构解决方案&#xff1a;用最小的工作量实现最大的价值 一切皆可虚拟化&#xff01;包括服务器在内。NetApp 虚拟服务器基础架构解决方案有助于加快数据访问速度、构建创新服务并简化部署&#xff0c;从而实现最大价值。 为什么选择 NetApp 的虚拟服务器基础架…

pytorch矩阵乘法总结

1. element-wise&#xff08;*&#xff09; 按元素相乘&#xff0c;支持广播&#xff0c;等价于torch.mul() a torch.tensor([[1, 2], [3, 4]]) b torch.tensor([[2, 3], [4, 5]]) c a*b # 等价于torch.mul(a,b) # tensor([[ 2, 6], # [12, 20]]) a * torch.tenso…

详解C++类对象(上篇)——超详细

目录 一&#xff0c;面向对象&面向过程的认识(简单了解即可&#xff0c;逐步认识&#xff09; 二&#xff0c; 类 2.1 类的引入 2.2 类的定义 1. struct 2. class 类的两种定义方式&#xff1a; 2.3 封装&类的访问限定符 1. 封装概念 2. 类的访问限定符 …

低代码如何不写代码创建表单和维护表单

工作表新建与修改 新建工作表的流程包含 新建工作表/编辑公祖表为工作表添加字段&#xff0c;例如“员工档案”表中有姓名、性别、年龄等字段为字段设置属性工作表布局工作表预览、保存、关闭 1、新建工作表/修改工作表 新建工作表 修改工作表 2、为工作表添加字段 添加字段 左…

关于C语言的一些杂记2

文章目录 sizeof运算符内容关于基本概念的问题关于一些语句的理解和分号的注意字符的理解关于输出格式的扩展 本文内容摘自C技能树一些优秀的博主 sizeof运算符内容 关于基本概念的问题 sizeof是C语言的关键字&#xff0c;它用来计算变量&#xff08;或数据类型&#xff09;在…

2.Hive创建数据库

1.数据库操作 1.1 创建数据库 create database test comment Just for test location /abcd with dbproperties(aaabbb); comment后面指的是注释&#xff1b;location后面是数据库存放路径&#xff1b;dbproperties代表了数据库的属性 ps.避免要创建的数据库已经存在错误&…

Vue最新状态管理工具Pinia——Pinia的安装与使用

Pinia从了解到实际运用——pinia的安装与使用 知识回调&#xff08;不懂就看这儿&#xff01;&#xff09;场景复现一、环境搭建1.创建项目2.安装pinia 二、基本使用1.创建pinia示例并挂载2.基本使用访问state使用getters使用actions 3.详细示例&#xff08;详细注解&#xff0…

【23】核心易中期刊推荐——视觉/图像感知与识别人工智能算法及应用​​​​​​​

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…

2023年盐城工学院五年一贯制专转本旅游学概论考试大纲

2023年盐城工学院五年一贯制专转本旅游学概论考试大纲 一、考核对象 本课程的考核对象是五年一贯制高职专升本酒店管理专业考生。 二、考核方式 本课程考核采用闭卷考试的方式。 三、考核要求 掌握旅游学的基本原理&#xff0c;掌握旅游学的核心概念&#xff0c;具备旅游…

Android性能监控:主循环性能统计LooperStatsService详解

作者&#xff1a;飞起来_飞过来 简介 在Android性能监控和优化领域&#xff0c;一个会影响App性能表现的因素与Handler Message Looper机制有关。当Looper里面的Message处理不及时、或数量太多占用过多处理时间时&#xff0c;可能会出现卡顿感&#xff0c;并且不容易定位到卡顿…

WoShop多商户进口出口跨境电商uniapp商城源码

源码介绍&#xff1a;WoShop多商户跨境电商商城系统将传统的分销、积分、拼团等传统销售模式和直播带货、短视频带货等新型电商营销完美融为一体&#xff0c;专注技术&#xff0c;支持二次开发&#xff0c;专为用户、技术商提供跨境电商技术解决方案。 WoShop跨境电商源码产品…

网络弹性基础知识和实践

什么是网络弹性 弹性是网络处理中断并继续以可接受的标准向用户提供服务的能力。网络运营可能会受到配置错误、断电或操作员错误等问题的威胁。当这种可能性发生时&#xff0c;最终用户无法访问网络&#xff0c;从而对组织产生负面影响。高度弹性的网络可以通过在网络运行中断…

chatgpt官网拒绝访问怎么处理-chatGPT入口正确打开方式

chatgpt官网拒绝访问的原因有哪些 OpenAI是一家人工智能技术公司&#xff0c;其官网是OpenAI最重要的宣传与交流平台之一。但是&#xff0c;有时访问OpenAI官网可能会受到限制或拒绝访问。以下是可能导致OpenAI官网拒绝访问的几个常见原因&#xff1a; IP地址被封锁: OpenAI网…

【Python】只需2行代码,轻松将PDF转换成Word(含示范案例)

文章目录 一、前期准备二、pdf2docx功能三、限制四、案例 一、前期准备 可将 PDF 转换成 docx 文件的 Python 库。该项目通过 PyMuPDF 库提取 PDF 文件中的数据&#xff0c;然后采用 python-docx 库解析内容的布局、段落、图片、表格等&#xff0c;最后自动生成 docx 文件。 …