FreeRTOS学习-共享资源的同步访问

2023-05-16

1. 简介

对于多任务的系统,对于某个共享资源(全局变量,外设等)的并发访问容易引起数据的不一致性,这将会导致一些意外的结果。而共享资源的同步访问则是为了解决这个问题而提供的一种同步机制,例如临界区、互斥锁等,他们的基本思想就是互斥访问。本文将介绍FreeRTOS中的临界区、互斥锁与递归互斥锁。

2. 临界区和挂起调度器

2.1. 基本临界区(Basic Critical Sections)

基本临界区:被taskENTER_CRITICAL()taskEXIT_CRITICAL()包围的代码区域。在临界区内的代码不会被其他任务和中断打断。

FreeRTOS也提供了ISR版本的临界区:taskENTER_CRITICAL_FROM_ISR()taskEXIT_CRITICAL_FROM_ISR()

具体的实现如下:

  1. 进入临界区(vPortEnterCritical()):

    {
        首先屏蔽小于系统最大优先级的所有中断。(`ulPortSetInterruptMask()`){
            屏蔽CPU的中断(`portCPU_IRQ_DISABLE()`)。
            设置中断优先级屏蔽系统寄存器为`configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT`。
            恢复CPU的中断屏蔽(`portCPU_IRQ_ENABLE()`)。
        }
        增加中断嵌套计数(`ulCriticalNesting++`)。
        如果调用者处于中断上下文,却调用了该接口,则会发生assert。
    }
    

    中断上下文的版本临界区进入流程:实际上只是做了任务上下文中的第一步,即只调用了ulPortSetInterruptMask()

  2. 退出临界区(vPortExitCritical()):

    {
        先检查当前临界区嵌套是否大于0,如果不是,则直接返回;否则需要执行下面的操作:
        减少临界区嵌套数(`ulCriticalNesting--`)。
        检查是否已经退出所有嵌套的临界区,即当前的嵌套数是否已减为0,如果是则恢复中断(portCLEAR_INTERRUPT_MASK()),即将中断屏蔽系统寄存器的值恢复为`portUNMASK_VALUE`。
    }
    

    中断上下文的版本临界区退出流程:是任务上下文中的简化版本,调用了vPortClearInterruptMask(ulNewMaskValue),即如果ulNewMaskValue为0,则恢复所有中断。

从实现可以看出,临界区是支持嵌套的,只有当嵌套深度为0时,才认为退出了临界区。需要注意的是,这个临界区深度是任务的上下文之一,也就是说,若发生了任务切换,临界区深度也会变化。

2.2. 挂起(锁定)调度器

临界区也可以通过挂起(或称锁定)调度器来实现。与基本临界区不同的是,它只能保护共享数据不会被多个任务并发访问,即中断依然可以打断任务执行而影响共享资源。

vTaskSuspendAll()可挂起调度器,挂起后将不发生上下文切换。

xTaskResumeAll()恢复调度器。具体的实现可参看挂起和恢复调度器。

3. 互斥锁和二值信号量

互斥锁(MUTual EXclution,缩写为MUTEX)是一种特殊的二值信号量,用于保护在多个任务之间共享的资源。

在FreeRTOS中,如果希望开启互斥锁模块,则需要设置configUSE_MUTEXES = 1

3.1. 优先级继承

在使用互斥锁进行共享资源保护时,通常会出现一种**优先级翻转(Priority Inversion)**的现象,如下图所示,即高优先级的任务等待低优先级任务所持有的互斥锁的时,被中间优先级的任务抢占的现象。因为总是存在低优先级的任务A先获取了互斥锁的可能性,此时,在高优先级的任务B申请该锁必然会进入Blocked状态,假设这时一个优先级介于任务A和任务B之间的任务C进入了Ready队列,那么任务C势必会导致任务A被抢占,间接地,原本应该属于任务B的时间片被任务C抢占了。从结果来看,一个高优先级的任务被低优先级的任务抢占了,这显然不是FreeRTOS的本意。

无优先级继承

为了避免优先级翻转导致的问题,通常会使用优先级继承的方案。其基本思路是:当有高优先级的任务等待低优先级任务所持有的互斥锁时,低优先级的任务继承高优先级任务的优先级,防止被中间优先级的任务中断。引入了优先级继承后,示意图如下:

优先级继承机制

由此,便可以看出二值信号量与互斥锁的区别:互斥锁支持优先级继承机制,而二值信号量不支持。

同时,与二值信号量不同的是,互斥锁存在锁的拥有者的概念,所以必须在同一个任务中申请和释放。显然,这也是符合常理的。

3.2.死锁

死锁是使用互斥锁时非常常见的问题。通常是通过软件设计来避免死锁。FreeRTOS应对死锁的方法就是鸵鸟策略,即不做任何处理。

3.3. 递归互斥锁

对于普通的互斥锁,如果任务尝试连续两次申请同一个锁时(前一次申请并没有释放),则会导致死锁。为了避免这种问题,FreeRTOS提供了递归互斥锁机制。在使用递归互斥锁时,可以多次申请同一个锁,释放时,也需要释放对应的次数,即xSemaphoreTakeRecursive()xSemaphoreGiveRecursive()应该成对出现。

3.4. 互斥锁与任务调度

当有高优先级的任务等待低优先级的任务所持有的互斥锁,一旦高优先级的任务获取到互斥锁,则会立即发生一次调度;

而如果持有锁的任务与等待该锁的任务拥有相同的优先级,当锁释放后,需要等待下一个Tick才会发生调度。

4. 互斥锁的使用

互斥锁是基于信号量实现的。因此,与信号量一样,这里也基于FreeRTOSv10的实现进行介绍。

4.1. 创建互斥锁

函数原型:需要设置configUSE_MUTEXES = 1

#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType );

该函数创建互斥锁,并返回互斥锁的句柄。

下面来看看其具体实现,活动图如下:

创建互斥锁活动图
可以看到,互斥锁在创建的时候,已经入队了一个消息,所以任务可以正常地获取刚创建的互斥锁。

4.2. 获取和释放互斥锁

互斥锁与二值信号量不同之处在于,互斥锁需要实现任务优先级继承机制。此外,互斥锁还有持有者的概念。下面则主要介绍一下这些行为的实现。其中关于获取互斥锁时包括如下操作:

  • 递增任务获取互斥锁的计数(void *pvTaskIncrementMutexHeldCount()):将任务当前占用的互斥锁计数增1,并返回任务的TCB。

    {
        如果当前有正在运行的任务(`pxCurrentTCB != NULL`),增加该任务的持有锁计数(`pxCurrentTCB->uxMutexesHeld++`)。
    
        返回当前任务的TCB(`pxCurrentTCB`)。
    }
    
  • 任务优先级继承(BaseType xTaskPriorityInherit( TaskHandle_t const pxMutexHolder )):锁的所有者任务将继承所有申请该锁的任务中,最高优先级的任务的优先级。FreeRTOSv10后,为了实现超时后恢复原来任务的优先级,该函数还需要返回是否发生了优先级继承。。活动图如下所示:

    任务优先级继承

  • 因为任务等待超时而导致优先级继承的恢复(void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask )):更新锁拥有者任务当前继承的优先级。
    因为任务等待超时而导致优先级继承的恢复

  • 获取因为某个等待信号量的任务超时后,目前所有等待该信号量的任务中最高的优先级(static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue )):

    {
        如果当前仍有其他任务在等待该信号量(`listCURRENT_LIST_LENGTH( &( pxQueue->xTasksWaitingToReceive ) ) > 0`),则获取目前最高的优先级(`uxHighestPriorityOfWaitingTasks = configMAX_PRIORITIES - listGET_ITEM_VALUE_OF_HEAD_ENTRY( &( pxQueue->xTasksWaitingToReceive ) )`)。
        否则,设置优先级为最低(`uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY`)。
    
        返回`uxHighestPriorityOfWaitingTasks`。
    }
    

在释放互斥锁时,包含如下操作:

  • 优先级的恢复(BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder )):结束优先级继承,并返回是否需要触发调度。

    优先级的恢复

  • 互斥锁的释放发生在static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition )中。

获取和释放互斥锁的API与信号量一致,详情请参看二值信号量小节。

4.3. 获取互斥锁的所有者

函数原型:

#define xSemaphoreGetMutexHolder( xSemaphore ) xQeueueGetMutexHolder( ( xSemaphore ) )
#define xSemaphroeGetMutexHolderFromISR( xSemaphore ) xQueueGetMutexHolderFromISR( ( xSemaphore ) )

这两个函数都会返回互斥锁的拥有者任务。

4.4. 删除互斥锁

删除互斥锁的API与信号量的一致,详情请参考删除信号量小节。

5. 递归互斥锁的使用

5.1. 创建递归互斥锁

函数原型:需要设置configUSE_RECURSIZE_MUTEXES = 1

#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queuQUEUE_TYPE_RECURSIVE_MUTEX )

该函数创建递归互斥锁,并返回递归互斥锁的句柄。

具体实现可参看创建互斥锁小节。

5.2. 获取递归互斥锁

函数原型:需要设置configUSE_RECURSIZE_MUTEXES = 1

#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )

BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait );

具体的实现如下:

{
    初始化`pxMutex = ( Queue_t * ) xMutex`。

    参数检查:`pxMutex != NULL`。

    如果该任务获取了该锁(`pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle()`):
    {
        将该锁的递归深度增一(`pxMutex->u.uxRecursiveCallCount++`)。
        设置返回值`xReturn = pdPASS`。
    }
    否则,即任务第一次获取锁:
    {
        和普通互斥锁一样获取锁(`xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait )`)。

        如果申请成功(`xReturn != pdFAIL`),则增加递归数(`pvMutex->u.uxRecursiveCallCount++`)。
    }

    返回`xReturn`。
}

从实现可以看出,递归互斥锁与正常互斥锁不同的是,当任务已经获取了该锁之后,它仅仅增加了递归计数,而不会使得任务进入Blocked状态。

5.3. 释放递归锁

函数原型:需要设置configUSE_RECURSIVE_MUTEXES = 1

#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) )

BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex );

下面看看其具体的实现:

{
    初始化`pxMutex = ( Queue_t * ) xMutex`。

    参数检查:Assert `pxMutex != NULL`。

    如果当前任务是递归锁的所有者(`pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle()`):
    {
        将锁的递归深度减一(`pxMutex->u.uxRecursiveCallCount--`)。

        如果锁的递归深度为0(`pxMutex->u.uxRecursiveCallCount == 0`):
        {
            正常释放该锁(`xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK )`)。
        }

        设置返回值`xReturn = pdPASS`。
    }
    否则,即当前任务不是递归锁的所有者:
    {
        设置返回值`xReturn = pdFAIL`。
    }

    返回`xReturn`。
}

从实现可以看到,只有当递归锁的递归深度递减为0时,才会真正的释放锁。

6. Gatekeeper Task(管程)

Gatekeeper task:是一个拥有和管理共享资源的任务。只有它自身能直接访问这个共享资源。其他的任务则需要通过与该任务进行通信来间接访问共享资源。

这是一种设计理念,在FreeRTOS中并没有给出直接的支持,需要用户编写自定义的任务实现。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS学习-共享资源的同步访问 的相关文章

  • Ubuntu16.04安装Docker的步骤

    大家好 xff0c 我是加摩斯 xff0c 觉得文章有帮助的小伙伴 xff0c 记得一键三连哟 xff5e 申明 xff1a 原创 xff0c 转载前请与我沟通 Docker是一种容器 xff0c 但它是轻量级的 目前在中国 xff0c 已
  • 【正则表达式】通俗易懂——正则表达式的零宽断言:?=、?<=、?!、?<! 的具体使用区别

    ps xff1a 想吐槽一下 xff0c 什么前瞻 xff0c 后顾 xff0c 负前瞻 xff0c 负后顾 xff0c 小白就想简单了解会用而已 xff0c 为啥网上很多明明很简单的东西非得写的那么 举的例子也那么 xff0c 对小白一点
  • 百度2023暑期实习第一场笔试编程题Java版

    1 题目内容 小红拿到了一个字符串 xff0c 她想知道这个字符串能否通过重新排列组成 Baidu 字符串 xff1f 注 xff1a 必须大小写完全相同 共有 t 组询问 输入描述 第一行输入一个正整数 t xff0c 代表询问次数 接下
  • 记录在UBUNTU上配置torch环境和CUDA,CUDNN安装的一周心酸史

    写在前面 xff1a 安装torch gpu版后是不是以为就可以安安心心进行训练了 xff1f xff01 错错错 xff01 xff01 xff01 经过种种试错 xff0c 了解到服务器上的CUDA不是全局配置的 xff01 需要自行安
  • Python学习笔记整理

    一 random库 计算机不能产生真正的随机数 xff0c 那么伪随机数也就被称为随机数 伪随机数 xff1a 计算机中通过采用梅森旋转算法生成的 xff08 伪 xff09 随机序列元素 python中用于生成伪随机数的函数库是rando
  • FSCE代码阅读和修改

    fsdet modelling roi heads roi heads py allow low quality matches
  • 目标检测笔记整理

    一 基本概念 1 任务背景 2 评价指标 3 数据集 二 传统目标检测方法 1 算法框架 1 区域选取 2 特征提取 3 分类回归 4 NMS 2 典型算法
  • FreeRTOS解析:TCB_t结构体及重要变量说明(Task-1)

    FreeRTOS解析 xff1a TCB t结构体及重要变量说明 xff08 Task 1 xff09 受博客限制 xff0c 如果您想获得更好的阅读体验 xff0c 请前往https github com Nrusher FreeRTOS
  • 深度学习与slam的小小思考

    深度学习与slam的小小思考 好久不发文章了 xff0c 主要是研究的东西因为要发论文 xff0c 所以发表之前很难分享出来 xff0c 加上研一上学期老师安排的工作比较琐碎 xff0c 所以更新的很少 不过研一下可能会好些 xff0c 立
  • IT运维人员必看!超全信息化建设之运维资料

    随着IT建设的不断深入和完善 计算机硬软件系统的运行维护已经成为了各行各业各单位领导和信息服务部门普遍关注和不堪重负的问题 xff0c 据统计 xff0c IT运维服务占到IT部门工作量的80 左右 IT运维普遍存在以下现象 xff1a 1
  • ovn原理与实践

    上篇文章简单介绍了ovs Open Virtual Switch 的原理和使用 xff0c 但是仔细想想 xff0c 单纯的ovs在云计算领域还存在着一些问题 xff0c 例如 xff1a ovs只能做二层转发 xff0c 没有三层的能力
  • 基于51单片机智能电子密码锁电路设计(毕设课设)

    基于AT89C51单片机的简易六位密码锁 输入三次锁定 密码输入错误蜂鸣器和LED灯报警 可以修改密码 资源下载 下载地址如下 xff08 912 xff09 xff1a https docs qq com doc DTlRSd01BZXN
  • CentOS7配置tomcat开机自启动

    文章目录 CentOS7配置tomcat开机自启动创建服务创建软连接刷新配置启动 重启 停止 状态开启自启动 关闭自启动 CentOS7配置tomcat开机自启动 创建服务 在 lib systemd system目录下创建一个脚本文件to
  • Jetson TX2 入门 ——打开摄像头

    Jetson TX2自带有一个板载摄像头 xff0c 当然也可以在TX2上连接usb摄像头和csi摄像头 1 打开板载摄像头 1 xff09 方法一 xff1a 视屏分辨率预览 nvgstcapture 1 0 prev res 61 3
  • 输入一个int型整数,按照从右向左的阅读顺序,返回一个不含重复数字的新的整数。

    package cn itcast test06 public class Test08 方法1 public static int method int num boolean flag 61 num gt 0 true false nu
  • 史上最强,Cas单点登录之服务端搭建

    目录 Cas Server搭建大浪淘沙 xff0c 沉者为金弱水三千 xff0c 只取一瓢 服务端配置介绍最常用的三个配置文件最常用的三个JSP页面 自定义数据源关闭HTTPS协议开启Restful接口1 加入jar包 2 配置web xm
  • C#创建子窗体、父窗体

    本文部分内容来自书籍 多文档界面 xff08 Multiple Document Interface xff0c MDI xff09 xff0c 其窗体用于同时显示多个文档 xff0c 每个文档显示在各自的窗口中 在MDI窗体中 xff0c
  • k8s部署报错network: failed to find plugin “flannel“ in path [/opt/cni/bin]]

    k8s在部署工作负载时 xff0c pod详情页提示以下问题 Failed to create pod sandbox rpc error code 61 Unknown desc 61 failed to set up sandbox c
  • VS各版本VC各版本对应关系

    Visual Studio 经过多年的发展 xff0c 有许多版本 xff0c 经常我们在拿到一份代码时不知道对应的VS版本 这时候可以打开工程目录下的vcproj vcxproj文件 xff0c 如下所示 span class token
  • 浅谈机场综合布线运维工作中的难点问题

    随着系统规模越来越大 体系越来越复杂 信息系统的作用越来越明显 运维保障的要求越来越高 xff0c 综合布线维护工作面临的问题也愈加凸显 以下以国内某大型机场在综合布线运维工作中面临的实际问题为例 xff0c 给大家分享综合布线运维利器 耐

随机推荐

  • 关于集成商转型

    前言 xff1a 集成商转型是一个老生常谈的话题 xff0c 集成服务一直以来就随着IT行业趋势而发展 xff0c IT行业从最初的硬件主导到硬件软件化 xff0c 再到软件服务化 xff0c 大趋势或将发展至服务产品化 xff0c 行业趋
  • centos系统开启vncserver

    yum y install tigervnc server 这个是安装命令 安装好以后输入vncserver就能开启 第一次开启会让设置密码 这个密码是用户vncclient 连接的时候需要输入的密码 vncserver kill 1 这个
  • 升降横移式立体车库设计机械设计毕业设计全套资料

    资源下载 下载地址如下1502 xff1a https docs qq com doc DTlRSd01BZXNpRUxl 本文选用了社会需求大 xff0c 最常见的一种立体车库 升降横移式立体停车库 介绍和分析了升降横移式立体停车库的结构
  • 在NVIDIA Jetson 平台上运行Deepstream速度慢的常见解决办法

    NVIDIA发布了最新的Deepstream 4 0 光说不练假把式 xff0c 光练不说傻把式 xff0c 不少用户发现在Jetson嵌入式平台上运行Deepstream会遭遇到速度变慢 xff0c 今天汇总几个常见解决方案 xff1a
  • Ubuntu18.04 安装ROS Melodic(官方版) 之不使用科学方法

    此前记录了官方安装流程 xff0c 参见Ubuntu18 04 安装ROS Melodic 官方版 xff0c 但是受限于无法访问raw githubusercontent com xff0c 需科学方法才能成功安装 xff0c 带来诸多不
  • 分层存储Stratis和管理快照

    分层存储Stratis Stratis称为卷管理文件系统 xff0c 以管理物理存储设备池的服务形式运行 xff0c 透明的为所创建的文件系统创建和管理卷 相较于Lvm操作简化了 xff0c Stratis可以动态管理卷层 xff0c 不需
  • 报错:AttributeError: NoneType object has no attribute device

    今天搞个测试 xff0c 测试是在horovod下进行的 问题就出在加载权重 xff08 参数 xff09 文件的地方 xff0c 加载权重命令load weights前要先build一下 xff0c 结果就build出这么一个错误 xff
  • ARM安装python模块

    ARM机器安装python模块绝对不不不不可以直接pip xff01 xff01 xff01 这里演示的是CentOS xff0c 其他linux系统类似 Step1 安装必备 yum install epel release yum in
  • k8s源码编译运行

    本文参考视频 xff1a https www bilibili com video BV1WK41137JA from 61 search amp seid 61 10758236638727752345 0 一定要用root用户 sudo
  • 腾讯vCUDA(gpu-manager)部署

    官网 xff1a https github com tkestack gpu manager 先夸赞一下腾讯的开源精神 xff0c 再吐槽一下 xff0c 官方README写的真是过于随意了 踩了一堆坑 xff0c 终于部署并测试成功了 下
  • 127.0.0.1:xxxx端口映射到物理机IP

    一个应用的Dashboard访问地址是127 0 0 1 8265 xff0c 但是我没有权限使用物理机的浏览器查看 xff0c 只能远程命令行访问机器 xff0c 该机器IP是10 18 127 2 xff0c 所以需要用如下命令映射一下
  • Debian解决Error opening terminal: xterm错误

    在Debian中n中无交互界面安装oneAPI遇见了Error opening terminal xterm报错 使用如下命令亲测可以解决 xff1a mkdir p usr share terminfo x cd usr share te
  • Nsight Compute(NCU) Scheduler Statistics 数据解读

    本文内容主要参考YT上的这个视频 xff1a https www youtube com watch v 61 nYSdsJE2zMs Warp硬件架构介绍 以Volta架构为例 xff0c 每个SM有4个Warp Scheduler xf
  • 【分享】那些免魔法的chatGPT,GPT最佳实践

    ChatGPT 问世 xff0c 犹如平地惊雷般 xff0c 在技术圈中引起了广泛讨论 作为全球最大的开发者社区 xff0c GitHub 平台也在近期诞生了多个 ChatGPT 相关的开源项目 xff0c 其数量之多 xff0c 可谓是见
  • tf.layers.dropout用法

    dropout xff1a 一种防止神经网络过拟合的手段 随机的拿掉网络中的部分神经元 xff0c 从而减小对W权重的依赖 xff0c 以达到减小过拟合的效果 注意 xff1a dropout只能用在训练中 xff0c 测试的时候不能dro
  • matplotlib中cla() clf() close()用途

    cla Clear axis即清除当前图形中的当前活动轴 其他轴不受影响 clf Clear figure清除所有轴 xff0c 但是窗口打开 xff0c 这样它可以被重复使用 close Close a figure window
  • 2020计算机视觉领域顶级会议时间表

    CVPR IEEE Conference on Computer Vision and Pattern Recognition Location United States Date June 2020 Deadline TBD about
  • 【论文翻译】-- GaitSet: Regarding Gait as a Set for Cross-View Gait Recognition

    本文是复旦大学发表于 AAAI 2019 的工作 截至目前CASIA B正确率最高的网络 英文粘贴原文 xff0c google参与翻译但人工为主 有不对的地方欢迎评论 粉色部分为本人理解添加 xff0c 非原文内容 目录 摘要 1 介绍
  • FreeRTOS学习-中断管理

    1 简介 中断管理是一个操作系统中最核心的功能之一 在FreeRTOS中 xff0c 中断总是可以打断task xff08 尽管是最高优先级的task xff09 xff0c 而task永远不可能打断中断ISR xff08 interrup
  • FreeRTOS学习-共享资源的同步访问

    1 简介 对于多任务的系统 xff0c 对于某个共享资源 xff08 全局变量 xff0c 外设等 xff09 的并发访问容易引起数据的不一致性 xff0c 这将会导致一些意外的结果 而共享资源的同步访问则是为了解决这个问题而提供的一种同步