RTOS中临界区嵌套保护的实现原理(基于RT-Thread)

news2024/11/24 14:52:18

0 前言

什么是临界区(临界段)?
裸机编程中由于不涉及线程和线程切换,因此没有临界区这一个概念。在RTOS中由于存在线程切换等场景,便有了临界区这个概念。简单来说,临界区就是不允许被中断的代码区域。什么时候代码执行的过程会被打断:一个是线程切换,一个是异常事件。对于RT-Thread而言实际上都是异常事件导致代码执行过程被中断,因为RT-Thread是在PendSV事件内进行线程切换的。
什么样的代码区域应该作为临界区保护起来?
假如我们有2个线程A和B,它们都有可能使用同一个串口向外打印数据,而我们希望线程A和B在执行printf这个操作时是不会被打断的,否则我们看到的串口打印数据将杂乱无章。下面是一个例子:

void thread_a(void *arg)
{
    for (;;)
    {
        printf("A thread!\r\n");
    }
}

void thread_b(void *arg)
{
    for (;;)
    {
        printf("B thread!\r\n");
    }
}

假设线程A和线程B是按照时间片轮转进行调度的,那么就有可能出现线程A正在打印"A thread!\r\n"时被线程B获得CPU使用权进而打印线程B的"B thread!\r\n",最后呈现在串口上的数据杂乱无章。这不是我们希望看到的结果,因此我们需要将printf作为临界区保护起来,以RT-Thread临界区保护API为例,修改后的代码如下:

void thread_a(void *arg)
{
    for (;;)
    {
        rt_enter_critical(); // 进入临界区
        printf("A thread!\r\n");
        rt_exit_critical(); // 退出临界区
    }
}

void thread_b(void *arg)
{
    for (;;)
    {
        rt_enter_critical(); // 进入临界区
        printf("B thread!\r\n");
        rt_exit_critical(); // 退出临界区
    }
}

1 RTOS中临界区保护的实现原理(基于RT-Thread)

前面我们已经知道导致代码段被中断的原因就是发生了异常事件,为了实现对临界区的保护可以选择最简单直接的方法:关闭全部中断(NMI fault和硬fault除外),为了避免直接关闭中断带来的不良影响,RT-Thread为我们提供了2种方案:
(1)开关全部中断
(2)使能/失能线程调度

1.1 开关全部中断

ARM的M3/M4/M7内核为了快速开关中断,专门设置了一条CPS指令,共有以下4种用法:

CPSID I ;PRIMASK=1;关中断
CPSIE I ;PRIMASK=0;开中断
CPSID F ;FAULTMASK=1, ;关异常
CPSIE F ;FAULTMASK=0 ;开异常

有关上述寄存器的定义如下:
在这里插入图片描述
这里我们并不想关闭硬件fault,因此只会使用CPSID I和CPSIE I这2条指令。对于没有出现临界区嵌套的情况,直接在进入临界区前关中断,退出临界区后关中断即可实现对临界区的保护。但如果临界区出现嵌套的话,只是单纯使用开关中断显然无法满足我们要求,看看RT-Thread如何解决这个问题的:

// 关全部中断
rt_base_t rt_hw_interrupt_disable(void);
;/*
; * rt_base_t rt_hw_interrupt_disable();
; */
rt_hw_interrupt_disable    PROC
    EXPORT  rt_hw_interrupt_disable
    MRS     r0, PRIMASK
    CPSID   I
    BX      LR
    ENDP
// 开全部中断
void rt_hw_interrupt_enable(rt_base_t level);
;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
rt_hw_interrupt_enable    PROC
    EXPORT  rt_hw_interrupt_enable
    MSR     PRIMASK, r0
    BX      LR
    ENDP

可以看到,我们使能中断时是需要传入一个rt_base_t类型参数,这个就是RT-Thread实现临界区嵌套保护的关键。我们使用时只需要按照如下格式进行嵌套保护即可,下面是一个一重临界区嵌套保护的示例:

rt_base_t level1;
rt_base_t level2;
void thread_a(void *arg)
{
    for (;;)
    {
        level1 = rt_hw_interrupt_disable(); 
        printf("A thread!\r\n");
        level2 = rt_hw_interrupt_disable();
        printf("A thread!!\r\n");
        rt_hw_interrupt_enable(level2); 
        printf("A thread!!!\r\n");
        rt_hw_interrupt_enable(level1);
    }
}

结合前面的汇编代码,我们很容易可以发现,只有在执行最后一个rt_hw_interrupt_enable操作时才会打开全部中断,真正实现嵌套临界区的保护。
它的实现原理就是使用全局变量保存进入临界区之前的全部中断开关状态,然后退出临界区时根据这个开关状态来进行全部中断开关。根据临界区嵌套保护语句的顺序,显然只有执行了最后一个rt_hw_interrupt_enable才会打开全部中断。

1.2 使能/失能线程调度

相比前面直接开关中断来实现临界区保护,使能/失能线程调度则显得“温柔”很多,它并不会长时间关闭中断对我们的一些中断事件(例如SysTick时间基准)造成影响,如果我们的线程并不会被中断服务函数内的操作影响,推荐使用使能/失能线程调度的方法去实现临界段保护,RT-Thread提供的相关函数如下:

// 失能线程调度
/**
 * This function will lock the thread scheduler.
 */
void rt_enter_critical(void)
{
    register rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    /*
     * the maximal number of nest is RT_UINT16_MAX, which is big
     * enough and does not check here
     */
    rt_scheduler_lock_nest ++;

    /* enable interrupt */
    rt_hw_interrupt_enable(level);
}
;/*
; * rt_base_t rt_hw_interrupt_disable();
; */
rt_hw_interrupt_disable    PROC
    EXPORT  rt_hw_interrupt_disable
    MRS     r0, PRIMASK
    CPSID   I
    BX      LR
    ENDP
// 使能线程调度
/**
 * This function will unlock the thread scheduler.
 */
void rt_exit_critical(void)
{
    register rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    rt_scheduler_lock_nest --;
    if (rt_scheduler_lock_nest <= 0)
    {
        rt_scheduler_lock_nest = 0;
        /* enable interrupt */
        rt_hw_interrupt_enable(level);

        if (rt_current_thread)
        {
            /* if scheduler is started, do a schedule */
            rt_schedule();
        }
    }
    else
    {
        /* enable interrupt */
        rt_hw_interrupt_enable(level);
    }
}
;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
rt_hw_interrupt_enable    PROC
    EXPORT  rt_hw_interrupt_enable
    MSR     PRIMASK, r0
    BX      LR
    ENDP

(1)rt_enter_critical函数
首先关闭全部中断,将rt_scheduler_lock_nest自增1,然后将中断状态还原到rt_enter_critical函数最开始的状态。
(2)rt_exit_critical函数
首先关闭全部中断,将rt_scheduler_lock_nest自减1,如果rt_scheduler_lock_nest<=0表示临界区保护结束需要退出临界区,这时rt_scheduler_lock_nest=0然后将中断状态还原到函数最开始的状态,如果调度已经开始则发起一次调度尽可能保证更高优先级线程能够得到及时运行。如果rt_scheduler_lock_nest>0表示临界区保护还未结束,将中断状态还原到函数最开始的状态。
实现临界区嵌套保护的核心就是rt_scheduler_lock_nest,借助嵌套临界区保护操作成对出现这一特点,使用rt_scheduler_lock_nest调度锁表征当前临界区保护嵌套执行情况,当rt_scheduler_lock_nest<=0时则可以退出临界区保护,因此rt_exit_critical()和rt_exit_critical()无需人为定义全局变量就可以实现临界区嵌套保护,使用起来更加方便。

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

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

相关文章

【操作系统】FCFS、SJF、HRRN、RR、EDF、LLF调度算法及python实现代码

文章目录 一、先来先服务调度算法&#xff08;FCFS&#xff09; 二、短作业优先调度算法&#xff08;SJF&#xff09; 三、高响应比优先调度算法&#xff08;HRRN&#xff09; 四、轮转调度算法&#xff08;RR&#xff09; 五、最早截至时间优先算法&#xff08;EDF&#…

ES学习日记(七)-------Kibana安装和简易使用

前言 首先明确一点&#xff0c;Kibana是一个软件&#xff0c;不是插件。 Kibana 是一款开源的数据分析和可视化平台&#xff0c;它是 Elastic stack 成员之一&#xff0c;设计用于和Elasticsearch 协作。您可以使用 Kibana 对 Elasticsearch 索引中的数据进行搜索&#xff0c;…

机器学习——卷积的变种

机器学习——卷积的变种 卷积神经网络&#xff08;Convolutional Neural Networks, CNNs&#xff09;是深度学习领域中最重要的技术之一&#xff0c;它在图像处理、语音识别、自然语言处理等领域取得了巨大成功。在CNN中&#xff0c;卷积层是最核心的组成部分之一&#xff0c;…

Java与Go的并发世界:理解Work Sharing与Work Stealing

概述 最近在理解Golang中的Per P概念&#xff0c;于是我就去Go的源码中挖呀挖&#xff0c;结果挖到了Go的调度器设计。 Golang的调度器设计文档提到了Go中的P(OS线程)调度器使用的是work-stealing调度算法论文。 论文中提到了两个多线程调度算法&#xff1a;work sharing和wor…

ETL工具-nifi干货系列 第六讲 处理器JoltTransformJSON

1、处理器作用 使用Jolt转换JSON数据为其他结构的JSON,成功的路由到success,失败的failure。处理JSON的实用程序不是基于流的&#xff0c;因此大型JSON文档转换可能会消耗大量内存。 Jolt&#xff1a;JSON 到 JSON 转换库&#xff0c;用 Java 编写&#xff0c;其中转换的 &qu…

自动驾驶---Motion Planning之轨迹Speed优化

1 背景 在之前的几篇文章中&#xff0c;不管是通过构建SL图《自动驾驶---Motion Planning之Path Boundary》&#xff0c;ST图《自动驾驶---Motion Planning之Speed Boundary》&#xff0c;又或者是构建SLT图《自动驾驶---Motion Planning之构建SLT Driving Corridor》&#xff…

vivado 配置存储器器件编程2

为双 QSPI (x8) 器件创建配置存储器文件 您可使用 write_cfgmem Tcl 命令来为双 QSPI (x8) 器件生成 .mcs 镜像。此命令会将配置数据自动拆分为 2 个独立 的 .mcs 文件。 注释 &#xff1a; 为 SPIx8 生成 .mcs 时指定的大小即为这 2 个四通道闪存器件的总大小。…

生产制造园区数字孪生3D大屏展示提升运营效益

在智慧园区的建设中&#xff0c;3D可视化管理平台成为必不可少的工具&#xff0c;数字孪生公司深圳华锐视点打造的智慧园区3D可视化综合管理平台&#xff0c;致力于将园区的人口、经济、应急服务等各项业务进行3D数字化、网络化处理&#xff0c;从而实现决策支持的优化和管理的…

前端二维码生成工具小程序:构建营销神器的技术解析

摘要&#xff1a; 随着数字化营销的不断深入&#xff0c;二维码作为一种快速、便捷的信息传递方式&#xff0c;已经广泛应用于各个领域。本文旨在探讨如何通过前端技术构建一个功能丰富、操作简便的二维码生成工具小程序&#xff0c;为企业和个人提供高效的营销支持。 一、引言…

【目标检测】YOLOv6 的网络结构,图解RepBlock重参数化

YOLOv6 是美团推出的&#xff0c;在这个版本里面&#xff0c;不再使用之前 YOLOv4 和 YOLOv5 的带 CSP 结构的 CSPDarknet-53 作为 backbone 了&#xff0c;而是在 RepVGG 的启发下&#xff0c;推出了新的 EfficientRep 作为 YOLOv6 的 backbone。 RepVGG 最重要的一点是&…

学透Spring Boot 003 —— Spring 和 Spring Boot 常用注解(附面试题和思维导图)

这是 学透 Spring Boot 专栏 的第三篇&#xff0c;欢迎关注我&#xff0c;与我一起学习和探讨 Spring Boot 相关知识&#xff0c;学透 Spring Boot。 从面试题说起 今天我们通过一道和Spring Boot有关的常见面试题入手。 面试题&#xff1a;说说 Spring Boot 中有哪些常用注解…

助力瓷砖生产智造,基于YOLOv5全系列参数【n/s/m/l/x】模型开发构建瓷砖生产制造场景下1280尺寸瓷砖表面瑕疵检测识别系统

砖生产环节一般经过原材料混合研磨、脱水、压胚、喷墨印花、淋釉、烧制、抛光&#xff0c;最后进行质量检测和包装。得益于产业自动化的发展&#xff0c;目前生产环节已基本实现无人化。而质量检测环节仍大量依赖人工完成。一般来说&#xff0c;一条产线需要配数名质检工&#…

Windows系统搭建TortoiseSVN客户端并实现无公网IP访问内网服务端

文章目录 前言1. TortoiseSVN 客户端下载安装2. 创建检出文件夹3. 创建与提交文件4. 公网访问测试 前言 TortoiseSVN是一个开源的版本控制系统&#xff0c;它与Apache Subversion&#xff08;SVN&#xff09;集成在一起&#xff0c;提供了一个用户友好的界面&#xff0c;方便用…

Mysql的基本命令

1 服务相关命令 命令描述systemctl status mysql查看MySQL服务的状态systemctl stop mysql停止MySQL服务systemctl start mysql启动MySQL服务systemctl restart mysql重启MySQL服务ps -ef | grep mysql查看mysql的进程mysql -uroot -hlocalhost -p123456登录MySQLhelp显示MySQ…

使用 Django 构建简单 Web 应用

当我们在使用Django构建Web应用时&#xff0c;通常将会涉及到多个步骤&#xff0c;从创建项目到编写视图、模板、模型&#xff0c;再到配置URL路由和静态文件&#xff0c;最后部署到服务器上。所以说如果有一个环节出了问题&#xff0c;都是非常棘手的&#xff0c;下面就是我们…

vim copilot插件安装使用

copilot简介 在使用不熟悉的开发语言或函数库进行开发工作时&#xff0c;虽然可以通过阅读开发文档或示例代码的方式学习开发&#xff0c;但这种方式学习成本较高、效率较低&#xff0c;且后续不一定会用上。 GitHub Copilot是一个由GitHub开发的机器学习工具&#xff0c;可以…

加密软件VMProtect教程:使用脚本-功能

VMProtect是新一代软件保护实用程序。VMProtect支持德尔菲、Borland C Builder、Visual C/C、Visual Basic&#xff08;本机&#xff09;、Virtual Pascal和XCode编译器。 同时&#xff0c;VMProtect有一个内置的反汇编程序&#xff0c;可以与Windows和Mac OS X可执行文件一起…

HTTP的介绍

一.什么是HTTP&#xff1f; Hyper Text Transfer Protocol,超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则。 二.HTTP的特点 &#xff08;1&#xff09;基于TCP协议&#xff1a;面向连接&#xff0c;安全 &#xff08;2&#xff09;基于请求-响应模型的&…

壁纸小程序Vue3(自定义头部组件)

1.自定义头部 coustom-nav <view class"layout"><view class"navbar"><view class"statusBar"></view><view class"titleBar"><view class"title">标题</view><view class&qu…

网络安全 | 什么是网络安全?

关注WX&#xff1a;CodingTechWork 网络安全 网络安全-介绍 网络安全是指用于防止网络攻击或减轻其影响的任何技术、措施或做法。网络安全旨在保护个人和组织的系统、应用程序、计算设备、敏感数据和金融资产&#xff0c;使其免受简单而不堪其绕的计算机病毒、复杂而代价高昂…