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

news2024/9/21 22:43:37

一、简介

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

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

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

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

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

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

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

Semaphores

二、运行机制

        信号量初始化为配置的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/506562.html

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

相关文章

企业官方网站怎么申请?

在数字化时代&#xff0c;企业官方网站是展示企业形象、宣传产品和服务的重要窗口。那么&#xff0c;企业官方网站怎么申请呢&#xff1f;下面是一些简单的步骤。 1、选择合适的网站建设平台 目前市面上有许多网站建设平台&#xff0c;企业需要根据自己的需求和预算选择适合自…

Vue3学习笔记(尚硅谷)

文章目录 一、创建vue3工程1-1、使用vite创建vue3项目1-1、安装开发者工具 二、常用Composition API2-1、setup2-2、ref函数2-3、reactive函数2-4、Vue3的响应式原理2-4-1.Vue2的响应式原理2-4-3.Vue3的响应式原理 2-5、reactive对比ref2-6、setup的两个注意点2-7、计算属性与监…

Excel中创建图表的快捷方式哪些

如果你在Excel中创建了很多图表&#xff0c;你可能正在寻找加快创建和格式化速度的快捷方式。以下是一些可以用于Excel图表的有用快捷方式。 一、在新工作表上创建新图表 要在新工作表上创建新图表&#xff0c;请执行以下操作&#xff1a; ​选择要用于创建图表的数据。按F1…

域适应 Domain adaption(1)

一、定义 1、无监督域自适应 经典机器学习假设训练集和测试集来自相同的分布。 然而&#xff0c;这个假设在现实世界的应用程序中可能并不总是成立&#xff0c;例如&#xff0c;数据来源不同。 这种情况下&#xff0c;域分布之间会存在差异&#xff0c;直接将训练好的模型应…

实时数仓项目开发过程中发现的几个问题和优化点(数据接入)

1、属性值被截断的问题 在数据实时接入阶段&#xff0c;使用NIFI ExecuteScript组件生成增、改、删SQL语句&#xff0c;将SQL语句放到了attribute中(详见视频教程http://mp.weixin.qq.com/s?__bizMzIyNzkwNDE4Nw&mid2247486672&idx1&sn41793a61dc5f7ca6b6f9a34b4…

供应链管理系统软件有哪些?这几款软件很不错

一、供应链管理系统解决什么问题 企业不断引进各类管理理念&#xff0c;落地运用各种信息化系统&#xff0c;然而依旧问题频出&#xff1a; 为什么交付还是常常延期&#xff1f;为什么成本依旧居高不下&#xff1f;为什么质量问题频频发生&#xff1f;为什么库存长期积压&…

SpringBoot启动原理

背景 1> 大家都知道SpringBoot是通过main函数启动的&#xff0c;这里面跟踪代码到处都没有找到while(true)&#xff0c;为什么启动后可以一直跑&#xff1f; 2> SpringBoot默认使用tomcat作为web容器。大家也可以通过在pom文件中exclusion掉tomcat&#xff0c;denpendenc…

113.【Vue-细刷-04】

Vue-03 (二十四)、浏览器存储(WebStorage)1.本地缓存(LocalStorage)(1). 模仿本地缓存-未用JSON转字符串(2).模拟本地缓存-使用JSON转字符串 2.会话缓存(Session Storage)(1).模拟会话缓存(2).会话缓存和本地缓存的区别(3).JSON转换与JSON解析 3.todos案列_本地缓存版(1).mount…

NVIDIA CUDA驱动安装

1 引言 因为笔记本电脑上运行Milvus图像检索代码&#xff0c;需要安装CUDA驱动。电脑显卡型号是NVIDIA GeForce GTX 1050 Ti Mobile, 操作系统是Ubuntu 20.04&#xff0c;内核版本为Linux 5.15.0-72-generic。 2 CUDA驱动测试 参考网上的资料&#xff1a;https://blog.csdn.…

车载测试ADAS-常用场景仿真软件

ADAS&#xff08;Advanced Driber Assistant System&#xff09;&#xff0c;高级驾驶辅助系统&#xff0c;先进驾驶辅 助系统&#xff0c;作用于辅助汽车驾驶&#xff0c;通过感知、决策和执行&#xff0c;帮助驾驶员察觉可能发生的危险&#xff0c;是提高安全性的主动安全技术…

数据结构与算法基础(王卓)(36):交换排序之快排【第三阶段:深挖解决问题】精华!精华!精华!!!重要的事情说三遍

目录 Review&#xff1a; 具体问题&#xff1a; 操作核心&#xff1a; 注&#xff1a; 操作分解&#xff1a; 操作实现&#xff1a; 问题&#xff08;1&#xff09;&#xff1a;进行不一样次数的 if / else 判断 问题&#xff08;2&#xff09;&#xff1a;通过判断条件…

Element-UI

目录 Layout 布局 按钮组件结合 el-icon 使用 单选框 复选框 日期组件 表格 分页 对话框 表单验证 Element-UI是由饿了么前端团队开发的一套基于Vue.js的桌面端组件库&#xff0c;包含了多个常用的UI组件&#xff0c;如按钮、输入框、表格、弹窗等&#xff0c;可以快速…

5.QT应用程序主窗口

本章代码见文末链接 主窗口框架 新建Qt Wisgets项目mymainwindow&#xff0c;类名默认MainWindow&#xff0c;基类默认QMainWindow 更改文字如图&#xff0c;如果中文无法直接输入&#xff0c;可以试试复制粘贴 “动作编辑器”中&#xff08;默认在右下角&#xff09;&…

AI数字人系统搭建源码

AI数字人系统的功能可以根据具体应用场景而定&#xff0c;以下是一些可能的功能&#xff1a; 语音识别和合成&#xff1a;将自然语言转换为机器可读的文本&#xff0c;或将机器生成的文本转换为自然语言的语音输出。 面部表情捕捉&#xff1a;利用摄像头等设备获取用户…

2023鲁大师评测沟通会:鲁大师尊享版登场、“鲁小车”正式上线

作为硬件评测界的“老兵”&#xff0c;鲁大师不仅有着十几年的硬件评测经验&#xff0c;并且一直都在不断地尝试、不断地推陈出新。在5月9日举行的“2023年鲁大师评测沟通会”上&#xff0c;鲁大师向大众展示了在过去一年间取得的成果。 PC业务迭代升级&#xff0c;鲁大师客户端…

day(2,3)-内核模块

内核模块上 主要内容 向内核添加新功能 内核模块基础代码讲解 内核模块多源文件编程 内核模块信息宏 一、向内核添加新功能 1.1 静态加载法&#xff1a; 即新功能源码与内核其它代码一起编译进uImage文件内 Kconfig是make menuconfig的界面配置文件 1.2动态加载法&am…

Yolov8改进---注意力机制:DoubleAttention、SKAttention,SENet进阶版本

目录 🏆🏆🏆🏆🏆🏆Yolov8魔术师🏆🏆🏆🏆🏆🏆 1. DoubleAttention 2. SKAttention 3.总结

2022-4-4基于单片机的MQ2烟雾传感器报警系统设计

基于单片机的MQ2烟雾传感器报警系统设计 源代码和仿真图及MQ2相关资料可在文末的链接中下载 该系统实现的功能&#xff1a;检测空气中的烟雾浓度并实现超阈值报警 该系统组成由&#xff1a; 单片机最小系统、ADC0832、MQ2烟雾传感器、LCD1602液晶模块、声光报警模块、按键模…

研究人员发现微软Azure API管理服务存在3个漏洞

微软Azure API管理服务中披露了三个新的安全漏洞&#xff0c;恶意行为者可能会滥用这些漏洞来访问敏感信息或后端服务。 据以色列云安全公司Ermetic称&#xff0c;这包括两个服务器端请求伪造(SSRF)漏洞和API管理开发人员门户中的一个不受限制的文件上传功能实例。 安全研究员…

【腾讯云 Finops Crane 集训营】Crane应用实战

文章目录 前言一、Crane是什么&#xff1f;二、Crane的特点三、Crane使用1、环境准备2、安装Crane3、访问dashboard4、页面展示5、功能应用 四、Crane的优势总结 前言 FinOps&#xff08;Financial Operations&#xff09;是一种管理云计算成本的方法&#xff0c;它强调将云计…