[FreeRTOS 内部实现] 信号量

news2024/11/13 12:17:06

文章目录

    • 基础知识
    • 创建信号量
    • 获取信号量
    • 释放信号量
    • 信号量 内部实现框图


基础知识

[FreeRTOS 基础知识] 信号量 概念


创建信号量

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

根据上面的定义,可知创建信号量本质就是创建队列。长度(第一个参数)为1,大小(第二个参数)为0

QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
    ->     if( uxItemSize == ( UBaseType_t ) 0 ) xQueueSizeInBytes = ( size_t ) 0;
    ->     pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );  // 动态分配大小,只有一个Queue_t结构体大小

在这里插入图片描述


获取信号量

通过队列获取信号量

#define xSemaphoreTake( xSemaphore, xBlockTime )        xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )
BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait )

for( ;; )
{
    taskENTER_CRITICAL();        // 关中断
    {
        const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;    // 获取当前信号量值
        
        if( uxSemaphoreCount > ( UBaseType_t ) 0 )                            // 信号量值大于0
        {
            traceQUEUE_RECEIVE( pxQueue );
            
            pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1;    // 信号量-1
            
            // 检查是否有其他任务阻塞等待信号量。
            if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
            {
                // 如果是,解除阻塞最高优先级的任务。
                // 1、将最高阻塞任务从xTasksWaitingToSend链表中移除;
                // 2、将最高阻塞任务从从DelayList移动到ReadyList链表
                if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
                {
                    queueYIELD_IF_USING_PREEMPTION();   // 让出CPU使用权
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
            
            taskEXIT_CRITICAL();   //开中断
            return pdPASS;        // 返回成功
        }
        else
        {
            if( xTicksToWait == ( TickType_t ) 0 ) // 是否愿意等待
            {
               // 不愿意等待


                taskEXIT_CRITICAL();   // 打开中断
                traceQUEUE_RECEIVE_FAILED( pxQueue );
                return errQUEUE_EMPTY;   // 返回队列空失败
            }
            else if( xEntryTimeSet == pdFALSE )
            {
                // 信号量计数为0,阻塞时间被指定,所以配置超时结构准备阻塞。
                vTaskInternalSetTimeOutState( &xTimeOut );
                xEntryTimeSet = pdTRUE;
            }
            else
            {
        
                mtCOVERAGE_TEST_MARKER();
            }
        }
    }
    taskEXIT_CRITICAL();

    vTaskSuspendAll();
    prvLockQueue( pxQueue );

    if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
    {
        if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
        {
            traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
            
            // 1、当前的任务加入到队列的xTasksWaitingToReceive链表中;
            // 2、当前的任务从ReadyList移动到DelayList
            vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
            prvUnlockQueue( pxQueue );
            if( xTaskResumeAll() == pdFALSE )
            {
            	portYIELD_WITHIN_API();
            }
            else
            {
            	mtCOVERAGE_TEST_MARKER();
            }
        }        
        else
        {

            prvUnlockQueue( pxQueue );
            ( void ) xTaskResumeAll();
        }
    }
    else
    {
        ...
    }

释放信号量

#define semGIVE_BLOCK_TIME                    ( ( TickType_t ) 0U )
#define xSemaphoreGive( xSemaphore )        xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
-> 
    for( ;; )
    {
    taskENTER_CRITICAL();    // 关中断 portDISABLE_INTERRUPTS();
    {

        if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )    // 判断当前信号量是否超过队列信号量长度
        {        
            traceQUEUE_SEND( pxQueue );
            xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
       // 不会写数据,但是会将 uxMessagesWaiting +1

            if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )          // 判断xTasksWaitingToReceive队列里是否有等待的任务
            {
                if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) // 1、将要写的任务从xTasksWaitingToReceive移除;2、将要写的任务从DelayList移动到ReadyList
                {

                    queueYIELD_IF_USING_PREEMPTION();    //让出CPU使用权
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }                                

            else if( xYieldRequired != pdFALSE )
            {

                queueYIELD_IF_USING_PREEMPTION();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        
        
        taskEXIT_CRITICAL();    // 开中断
        return pdPASS;          // 返回成功
    }
    else
    {
        if( xTicksToWait == ( TickType_t ) 0 )
        {
        
            taskEXIT_CRITICAL();
                
            traceQUEUE_SEND_FAILED( pxQueue );
            return errQUEUE_FULL;         // 返回队列已满
        }
        else if( xEntryTimeSet == pdFALSE )
        {
       
            vTaskInternalSetTimeOutState( &xTimeOut ); // 阻塞时间被指定,所以配置超时结构。 
            xEntryTimeSet = pdTRUE;
        }
        else
        {
        
        mtCOVERAGE_TEST_MARKER();
        }
    }
}
taskEXIT_CRITICAL();    // 开中断


vTaskSuspendAll();
prvLockQueue( pxQueue );

        
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
    if( prvIsQueueFull( pxQueue ) != pdFALSE )
    {
        traceBLOCKING_ON_QUEUE_SEND( pxQueue );
        // 1、当前的任务加入到队列的xTasksWaitingToSend链表中;
        // 2、当前的任务从ReadyList移动到DelayList                
        vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
        prvUnlockQueue( pxQueue );
    
        if( xTaskResumeAll() == pdFALSE )
        {
            portYIELD_WITHIN_API();
        }
    }
    else
    {

        prvUnlockQueue( pxQueue );
        ( void ) xTaskResumeAll();
    }
}
else
{

    prvUnlockQueue( pxQueue );
    ( void ) xTaskResumeAll();
    
    traceQUEUE_SEND_FAILED( pxQueue );
    return errQUEUE_FULL;
}
}

信号量 内部实现框图

在这里插入图片描述

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

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

相关文章

C++精解【6】

文章目录 eigenMatrix基础例编译时固定尺寸运行指定大小 OpenCV概述 eigen Matrix 基础 所有矩阵和向量都是Matrix模板类的对象。向量也是矩阵&#xff0c;单行或单列。Matrix模板类6个参数&#xff0c;常用就3个参数&#xff0c;其它3个参数有默认值。 Matrix<typename…

LGY-110G零序电压继电器 导轨安装 约瑟JOSEF

110系列零序电压电流继电器 系列型号: LGY-110零序过电压继电器&#xff1b;LGY-110/AC零序过电压继电器&#xff1b; LGL-110零序过电流继电器&#xff1b;LGL-110/AC零序过电流继电器&#xff1b; 1 应用 DY-110 型低电压继电器用于判别线路和电力设备的电压降低&#xf…

达梦数据守护集群部署

接上篇 达梦8单机规范化部署 https://blog.csdn.net/qq_25045631/article/details/139898690 1. 集群规划 在正式生产环境中&#xff0c;两台机器建议使用统一配置的服务器。使用千兆或千兆以上网络。 两台虚拟机各加一块网卡&#xff0c;仅主机模式&#xff0c;作为心跳网卡…

伸展树(数据结构篇)

数据结构之伸展树 伸展树 概念&#xff1a; 伸展树是一颗对任意一个节点被访问后&#xff0c;就经过一系列的AVL树的旋转操作将该节点放到根上的特殊二叉查找树。伸展树能保证对树操作M次的时间复杂度为O(MlogN)&#xff0c;而当一个查找树的一个节点刚好处于查找树最坏的情…

Vite: 关于预构建的毫秒级响应

概述 在我们的项目代码中&#xff0c;我们所说的模块代码其实分为两部分 一部分是源代码&#xff0c;也就是业务代码另一部分是第三方依赖的代码&#xff0c;即 node_modules 中的代码 Vite 是一个提倡 no-bundle 的构建工具&#xff0c;相比于传统的 Webpack能做到开发时的模…

主机游戏也可以上云桌面玩了?

最近steam夏季促销活动也快到了&#xff0c;对于很多钟情于主机游戏的小伙伴们&#xff0c;是不是也在摩拳擦掌了&#xff1f; 但有时候现实想愉快地玩到自己想玩的游戏实在是太难了&#xff01; 当你一直关注的新游戏终于上线Steam时&#xff0c;你的钱包是这样的… 而游戏的…

elementUI相关知识及搭建使用过程

​​​​​​ 目录 ​​​​​​ 一.elementUI相关的知识 1.什么是elementUI 2.如何在创建的项目中使用elementUI的组件(1)安装 ​ (2)在项目的main.js中引入elementUI (3)使用elementui里的组件 一.elementUI相关的知识 1.什么是elementUI Element&#xff0c;一套为开…

【管理咨询宝藏136】RB大型卡车集团供应链体系优化设计方案

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏136】RB大型卡车集团供应链体系优化设计方案 【格式】PDF版本 【关键词】罗兰贝格、供应链管理、运营提升 【核心观点】 - 针对前两个模块&…

Charles配置与API数据抓取

2024软件测试面试刷题&#xff0c;这个小程序&#xff08;永久刷题&#xff09;&#xff0c;靠它快速找到工作了&#xff01;&#xff08;刷题APP的天花板&#xff09;-CSDN博客跳槽涨薪的朋友们有福了&#xff0c;今天给大家推荐一个软件测试面试的刷题小程序。https://blog.c…

基于STM32的智能健康监测手表

目录 引言环境准备智能健康监测手表系统基础代码实现&#xff1a;实现智能健康监测手表系统 4.1 数据采集模块4.2 数据处理与分析4.3 通信模块实现4.4 用户界面与数据可视化应用场景&#xff1a;健康监测与管理问题解决方案与优化收尾与总结 1. 引言 智能健康监测手表通过使…

美团SaaS技术部测开,复捞我开摆

美团SaaS技术部测开&#xff0c;复捞我开摆 4.3 80min 项目实习你主要承担的部分你做的项目技术实现是什么样的&#xff0c;前后端是怎么做出来的自动登录功能的架构软件测试全流程你有了解吗搭建测试环境是否可有可无哪些举措是你牵头做的发现了什么问题在代码核验阶段你如何…

C++STL梳理

CSTL标准手册&#xff1a; https://cplusplus.com/reference/stl/ https://cplusplus.com/reference/vector/vector/at/ 1、STL基础 1.1、STL基本组成(6大组件13个头文件) 通常认为&#xff0c;STL 是由容器、算法、迭代器、函数对象、适配器、内存分配器这 6 部分构成&…

【消息队列】Kafka学习笔记

概述 定义 传统定义: 一个分布式的, 基于发布订阅模式的消息队列, 主要应用于大数据实时处理领域新定义: 开源的分布式事件流平台, 被用于数据管道/流分析/数据集成 消息队列的使用场景 传统消息队列的主要应用场景包括: 削峰: 解耦: 异步: 两种模式 点对点模式 发布/订…

Java基础:IO流

目录 一、定义 1.引言 2.分类 &#xff08;1&#xff09;按照流的方向分 &#xff08;2&#xff09;按操作文件的类型分 3.体系结构 二、字节流&#xff08;以操作本地文件为例&#xff09; 1. FileOutputStream 类 &#xff08;1&#xff09;定义 &#xff08;2&am…

【Linux】Linux编译器(gcc,g++)与动静态链接库

对于一个 C 程序&#xff0c;从源文件到形成可执行程序一共要进行四步&#xff1a;预处理、编译、汇编、链接 。 接下来&#xff0c;我们用 gcc 分别演示这四个过程。 一、预处理(进行宏替换) 预处理中&#xff0c;需要完成头文件的展开、宏替换、去注释、条件编译等工作。 …

说一说三大运营商的流量类型,看完就知道该怎么选运营商了!

说一说三大运营商的流量类型&#xff0c;看完就知道该怎么选运营商了&#xff1f;目前三大运营商的流量类型大致分为通用流量和定向流量&#xff0c;比如&#xff1a; 中国电信&#xff1a;通用流量定向流量 电信推出的套餐通常由通用流量定向流量所组成&#xff0c;通用流量…

《数字图像处理》实验报告一

一、实验任务与要求 1、用 matlab 编写空间域点处理操作处理给定的几幅图像&#xff0c;要求&#xff1a; 使用 imread 读取当前工作目录下的图像设计点处理操作并用代码实现处理用 imnshow 显示处理后的图像用 imwrite 保存处理后的图像 2、提交内容&#xff1a;m文件 实验…

假冒国企现形记:股权变更视角下的甄别分析

启信慧眼-启信宝企业版 假冒国企公告2024-06-07&#xff0c;中粮集团有限公司官网发布《关于冒名中粮企业名单公告》。公告显示&#xff0c;”有不法分子通过伪造相关材料等方式&#xff0c;以我集团子公司名义开展业务&#xff0c;进行虚假宣传。经核实&#xff0c;上述公司假…

【SpringCloud】API网关(Spring Cloud Gateway)

本文基于上一篇http://t.csdnimg.cn/q3YrK 使用抽取的方案使用feign的基础上使用Spring Cloud Gateway。 API网关 API网关&#xff08;简称网关&#xff09;也是一个服务&#xff0c;通常是后端服务的唯一入口。它就像是整个微服务架构的门面&#xff0c;所有的外部客户端访问…

Python学习笔记13:进阶篇(二),类的继承与组合

类的继承 我们在编写一系列的类的时候&#xff0c;会发现这些类很相似&#xff0c;但是又有各自的特点和行为。在编写这些类的时候&#xff0c;我们可以把相同的部分抽象成一个基类&#xff0c;然后根据其他不同的特点和行为&#xff0c;抽象出子类&#xff0c;继承这个基类。…