【BES2500x系列 -- RTX5操作系统】深入探索CMSIS-RTOS RTX -- 同步与通信篇 -- 信号量和互斥锁 --(三)

请添加图片描述

  • 💌 所属专栏:【BES2500x系列】

  • 😀 作  者:我是夜阑的狗🐶

  • 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询!

  • 💖 欢迎大家:这里是CSDN,我总结知识的地方,喜欢的话请三连,有问题请私信 😘 😘 😘

您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!🤩 🤩 🤩

请添加图片描述


前言

  大家好,又见面了,我是夜阑的狗🐶,本文是专栏【BES2500x系列】专栏的第3篇文章;
  今天开始学习BES2500x系列的一天💖💖💖,开启新的征程,记录最美好的时刻🎉,每天进步一点点。
  专栏地址:【BES2500x系列】, 此专栏是我是夜阑的狗对BES2500x系列开发过程的总结,希望能够加深自己的印象,以及帮助到其他的小伙伴😉😉。
  如果文章有什么需要改进的地方还请大佬不吝赐教👏👏。


<<【系列文章索引】>>

1 介绍

  前面已经学习了 RTX 系统中线程管理的创建和使用,当然在操作系统中除了线程管理外,线程之间的同步与通信也是至关重要的,话不多说,那接下来就同步与通信都有那些内容吧,让我们原文再续,书接上回吧。

在这里插入图片描述

2 功能特性

  在实时操作系统(RTOS)中,任务管理和同步通信是关键组件,它们确保系统的高效和有序执行。本文将探讨这些概念,特别是线程管理、信号量、互斥锁、消息队列和邮箱处理。

  • 任务管理:RTX提供任务创建、调度和优先级管理,确保任务按照优先级及时执行。
  • 同步与通信:包括信号量、互斥锁、消息队列和邮箱,促进任务间的同步和数据交换。
  • 内存管理:内存池和动态内存分配,有效管理有限的系统资源。
  • 定时器服务:虚拟和硬件定时器,支持周期性任务和一次性事件触发。
  • 中断处理:保证中断服务的快速响应,同时保持任务的上下文安全。
  • 线程安全:通过内核级保护机制,防止多线程环境下的数据竞争和死锁。

3 同步与通信

  在实时操作系统(RTOS)中,同步与通信是确保任务间协调执行和数据交换的关键机制。以下是关于实时操作系统中同步与通信的几个核心概念和方法:

3.1 同步(Synchronization)

  同步指的是确保系统中的多个任务或事件按照预定的顺序执行的技术。在多任务或多线程的嵌入式环境中,同步机制用于防止数据竞争和不一致问题,确保共享资源的正确访问。常见的同步方法包括:

序号方法说明
1互斥锁(Mutex)用于保护临界区,确保同一时间只有一个任务可以访问共享资源。
2信号量(Semaphore)不仅可以实现互斥访问,还可以用于计数资源的可用性,支持多任务等待同一资源。
3事件标志(Event Flags)用于任务间的简单状态通信,一个任务可以设置某些事件标志,另一个任务可以等待这些标志被设置。
4条件变量(Condition Variables)与互斥锁一起使用,允许任务等待某个条件变为真。
5原子操作不可中断的操作,用于避免多任务环境下的数据冲突。

3.2 设计原则

  • 最小化阻塞:选择适当的同步和通信机制,减少任务等待时间,提高系统响应速度。
  • 确定性:确保所有同步和通信操作的时间复杂度是可预测的,以满足实时性要求。
  • 资源效率:考虑内存和CPU开销,选择最适合应用需求的机制。
  • 错误处理:实现健壮的错误处理逻辑,比如超时处理、死锁预防等。

3.3 实例应用

  在多传感器数据融合的场景中,可以使用消息队列来收集来自不同传感器的任务数据,并在主控制任务中进行融合处理。在电机控制应用中,通过互斥锁保护对电机控制寄存器的访问,避免并发写入导致的控制混乱。
  综上所述,实时操作系统中的同步与通信机制是实现可靠、高效、实时性应用的基础,正确地设计和应用这些机制对于保证系统性能和稳定性至关重要。

4 信号量

  我们接下来将详细介绍信号量和互斥锁。信号量是一种同步机制,用于控制对共享资源的访问。它可以是计数信号量或二进制信号量,用于管理多个线程的并发访问。

4.1 定义信号量

  信号量是一个计数器,用于控制对公共资源的访问。计数信号量可以有多个资源,二进制信号量只有两个状态:空闲(1)和占用(0)。
  一般在文件开头会看到这样的定义:osSemaphoreDef

  • 代码
// 定义一个名为app_rtx_test_semaphore的互斥量
osSemaphoreDef(app_rtx_test_semaphore);

// 定义一个变量app_rtx_test_semaphore_id,用于存储互斥量的ID
// 在实际操作中,这个ID将用于在运行时访问和操作互斥量
osSemaphoreId app_rtx_test_semaphore_id = NULL;
/**
 * 定义一个OS semaphore的结构体实例。
 * 这是一个宏,用于静态定义一个互斥量( semaphore),以便在操作系统中使用。
 * 
 * @param name 互斥量的名称,用于标识和访问该互斥量。
 */
#define osSemaphoreDef(name) \
static StaticSemaphore_t os_semaphore_cb_##name; /* 在静态内存中定义一个静态互斥量变量 */ \
const osSemaphoreDef_t os_semaphore_def_##name = \
{ NULL, 0U, (&os_semaphore_cb_##name), sizeof(StaticSemaphore_t) } /* 定义互斥量结构体,包含互斥量的控制块地址和其他必要信息 */
  • 参数/函数讲解
序号参数/函数说明
1osSemaphoreId定义变量来存储信号量的 ID
2osSemaphoreDef定义信号量结构体

4.2 创建信号量

  创建信号量通常通过调用 osSemaphoreCreate() 函数实现,传入定义好的信号量结构体和初始值。

  • 代码
/**
 * 初始化名为app_rtx_test_semaphore的信号量。
 * 如果互斥量尚未创建,此函数将创建它并将其ID存储在全局变量app_rtx_test_semaphore_id中。
 *
 * @return 总是返回0,表示初始化成功。
 */
int app_rtx_test_sem_init(void) 
{
    // 检查互斥量ID是否为空
    if (app_rtx_test_semaphore_id == NULL) {
        // 创建互斥量,传入之前定义的信号量结构体和初始信号量值(这里是1)
        app_rtx_test_semaphore_id = osSemaphoreCreate(osSemaphore(app_rtx_test_semaphore), 1);
    }

    // 初始化成功,返回0
    return 0;
}
  • 参数/函数讲解
序号参数/函数说明
1osSemaphoreCreate创建信号量
2osSemaphore获取前面定义的信号量结构体

4.3 获取信号量

  线程通过 osSemaphoreWait() 函数请求信号量,如果资源可用,函数会立即返回;否则,线程会被挂起,直到资源可用。

  • 代码
/**
 * 请求获取app_rtx_test_semaphore互斥量的锁。
 * 此函数会阻塞直到获得锁,或者在等待过程中被信号中断。
 *
 * @return 总是返回0,表示锁获取成功。
 */
int app_rtx_test_sem_lock(void) 
{
    // 等待获取互斥量锁,如果资源不可用,将一直等待
    osSemaphoreWait(app_rtx_test_semaphore_id, osWaitForever);

    // 锁获取成功,返回0
    return 0;
}
  • 参数/函数讲解
序号参数/函数说明
1osSemaphoreWait等待获取信号量

4.4 释放信号量

  当线程完成对资源的操作后,通过 osSemaphoreRelease() 函数释放信号量,让其他等待的线程有机会获取。

  • 代码
/**
 * 释放app_rtx_test_semaphore互斥量的锁。
 * 此函数允许其他任务获取该互斥量。
 *
 * @return 总是返回0,表示锁释放成功。
 */
int app_rtx_test_sem_unlock(void) 
{
    // 释放信号量锁,允许其他任务使用
    osSemaphoreRelease(app_rtx_test_semaphore_id);

    // 锁释放成功,返回0
    return 0;
}
  • 参数/函数讲解
序号参数/函数说明
1osSemaphoreRelease释放信号量,允许其他任务使用

5 互斥锁

  互斥锁是另一种同步机制,确保同一时间只有一个线程访问特定资源。

5.1 定义

  一般在文件开头会看到这样的定义:osMutexDef

  • 代码
// 声明一个互斥锁实例,使用osMutexDef宏并传入名称app_rtx_test_mutex
osMutexDef(app_rtx_test_mutex);

// 定义一个osMutexId类型的变量,用于存储互斥锁的ID
// 在初始化后,该变量将持有app_rtx_test_mutex的标识符,以便在需要时进行锁定和解锁操作
osMutexId app_rtx_test_mutex_id = NULL;
/**
 * 定义一个互斥锁的宏。
 * 
 * 该宏用于静态定义一个互斥锁(Mutex),支持递归和优先级继承特性。它通过提供一个名称来创建
 * 该互斥锁的相关静态数据结构。
 * 
 * @param name 互斥锁的名称。该名称将用于为互斥锁的控制块和定义结构体命名。
 */
#define osMutexDef(name) \
static StaticSemaphore_t os_mutex_cb_##name; /* 控制块静态存储区 */ \
const osMutexDef_t os_mutex_def_##name = \
{ NULL, osMutexRecursive | osMutexPrioInherit, (&os_mutex_cb_##name), sizeof(StaticSemaphore_t) } /* Mutex定义结构体 */
  • 参数/函数讲解
序号参数/函数说明
1osMutexId定义用于存储互斥锁的ID
2osMutexDef定义互斥锁结构体

5.2 创建

  创建互斥锁通常使用 osMutexCreate() 函数,传入互斥锁的定义。

  • 代码
/**
 * 初始化指定的互斥锁。
 *
 * 此函数负责初始化之前通过osMutexDef定义的app_rtx_test_mutex互斥锁。
 * 如果该互斥锁尚未被创建(即其ID为NULL),则会调用osMutexCreate进行创建。
 *
 * @return 返回0表示初始化成功。由于此实现未处理失败情况,故假设初始化总是成功的。
 */
int app_rtx_test_mutex_init(void) 
{
    // 检查互斥锁是否已初始化
    if (app_rtx_test_mutex_id == NULL) { 
        // 使用osMutexDef定义的结构体创建互斥锁,并将其ID保存到app_rtx_test_mutex_id中
        app_rtx_test_mutex_id = osMutexCreate((osMutex(app_rtx_test_mutex))); 
    } 

    // 初始化成功,返回0
    return 0; 
}
  • 参数/函数讲解
序号参数/函数说明
1osMutexCreate创建互斥锁
2osMutex获取前面定义的互斥锁结构体

5.3 加锁

  线程使用 osMutexLock() 函数请求互斥锁,如果锁未被占用,线程会立即加锁;否则,线程会被挂起等待。

  • 代码
/**
 * @brief          尝试获取互斥锁
 * 
 * @description    该函数用于请求一个名为`app_rtx_test_mutex_id`的互斥锁。
 *                 使用`osWaitForever`参数表示函数将一直等待直到获得锁。
 *                 
 * @param[in]      无
 * 
 * @return         返回值0表示成功获取了互斥锁。
 * 
 * @note           若在多线程环境下其他线程已持有该锁,则调用此函数的线程会被挂起,
 *                 直到锁被释放。这可能导致当前线程阻塞,因此使用`osWaitForever`需谨慎。
 */
int app_rtx_test_mutex_lock(void)
{
    // 调用osMutexWait来请求互斥锁,如果无法立即获取则无限等待
    osMutexWait(app_rtx_test_mutex_id, osWaitForever); 
    
    // 成功获取锁后返回0
    return 0; 
}
  • 参数/函数讲解
序号参数/函数说明
1osMutexWait等待获取互斥锁

  这个函数 app_rtx_test_mutex_lock 用于获取一个已知的互斥锁 app_rtx_test_mutex 。它调用了实时操作系统(RTOS)的API osMutexWait ,传入互斥锁的句柄和一个标志 osWaitForever,表示如果锁不可用,当前任务将被挂起,直到能够获取锁为止。如果成功获取了锁,函数返回0。

5.4 解锁

  线程完成操作后,使用 osMutexUnlock() 函数释放互斥锁,允许其他线程获取。

  • 代码
/**
 * 释放app_rtx_test_mutex互斥锁。
 *
 * 当前任务持有互斥锁时,此函数将释放它,允许其他等待该锁的任务继续执行。
 *
 * @return 返回0表示成功释放锁。由于此实现未处理失败情况,故假设锁总是能被成功释放。
 */
int app_rtx_test_mutex_unlock(void) 
{
    // 调用RTX API释放互斥锁
    osMutexRelease(app_rtx_test_mutex_id); 

    // 释放锁成功,返回0
    return 0; 
}
  • 参数/函数讲解
序号参数/函数说明
1osMutexRelease释放互斥锁,允许其他任务使用

<<【系列文章索引】>>

请添加图片描述


总结

  感谢观看,这里就是 同步与通信篇 – 信号量和互斥锁的讲解,如果觉得有帮助,请给文章点个赞吧,让更多的人看到。🌹 🌹 🌹

在这里插入图片描述

  也欢迎你,关注我。👍 👍 👍

  原创不易,还希望各位大佬支持一下,你们的点赞、收藏和留言对我真的很重要!!!💕 💕 💕 最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!下期再见。🎉

更多专栏订阅:



订阅更多,你们将会看到更多的优质内容!!

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是夜阑的狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值