RT-Thread互斥量

发布于:2024-05-05 ⋅ 阅读:(26) ⋅ 点赞:(0)


前言

本篇文章将为大家讲解RT-Thread中的互斥量概念和使用方法,以及和信号量的对比,及RT-Thread中的互斥量和FreeRTOS中互斥量的比较。

一、RT-Thread互斥量的概念

在 RT-Thread 中,互斥量(Mutex,互斥锁)是一种用于多线程编程的同步原语,用于确保共享资源在任意时刻只被一个线程访问。互斥量的概念和作用如下:

1.确保共享资源的互斥访问:互斥量用于保护对共享资源的访问,确保在任意时刻只有一个线程可以访问该资源。这样可以避免多个线程同时修改共享资源而导致的数据不一致或竞态条件。

2.实现临界区保护:互斥量通常用于实现临界区保护,将对共享资源的访问限制在临界区内。只有获取了互斥量的线程才能进入临界区,其他线程需要等待互斥量释放后才能进入。

3.防止死锁和资源竞争:RT-Thread 的互斥量实现通常会考虑死锁和资源竞争的问题。互斥量的实现会遵循一定的规则和算法,以确保线程获取互斥量的顺序和释放的顺序是一致的,从而避免死锁的发生。

4.提供更细粒度的同步机制:相比信号量,互斥量提供了更细粒度的同步机制。它通常用于保护单个共享资源或临界区,而信号量则更适合用于限制对资源数量的访问。

二、互斥量和信号量的区别

在RT-Thread中,互斥量(Mutex)和信号量(Semaphore)都是用于线程同步的机制,但它们在功能和使用上有一些区别。

互斥量(Mutex)

  1. 功能:互斥量用于保护共享资源,确保在任何时候只有一个任务可以访问该资源。当一个任务持有互斥量时,其他任务需要访问该资源会被阻塞,直到互斥量被释放。

  2. 特点

    • 互斥量可以看作是一种二进制信号量,只有两种状态:被锁定和未锁定。
    • 只有成功获取互斥量的任务才能继续执行临界区代码,其他任务在获取失败时会被阻塞。
    • 互斥量可以解决优先级翻转问题,即当一个低优先级任务持有互斥量时,系统会提升其优先级到需要访问该资源的最高优先级任务的优先级,以防止高优先级任务被阻塞。
  3. 使用场景:适用于需要临界区保护的情况,例如对共享资源的读写操作。

信号量(Semaphore)

  1. 功能:信号量用于控制资源的访问数量,可以允许多个任务同时访问共享资源。信号量的值表示可用资源的数量,任务在访问资源前会尝试获取信号量,成功获取后资源数量减一,释放资源后信号量增加。

  2. 特点

    • 信号量是一种计数信号量,可以有多个状态。
    • 可以用于限制同时访问共享资源的任务数量。
    • 不能解决优先级翻转问题,因为对于信号量而言,获取信号量的任务只是减少信号量计数,并不会影响任务的优先级。
  3. 使用场景:适用于控制资源访问数量的情况,例如控制同时连接的任务数量、限制缓冲区大小等。

互斥量适用于保护临界资源的情况,并且可以解决优先级翻转问题;而信号量适用于控制资源访问数量的情况,但不能解决优先级翻转问题。

优先级翻转:

优先级翻转(Priority Inversion)是指一个较低优先级的任务(Task A)持有了一个较高优先级任务(Task B)所需要的资源而导致的情况,从而使得较高优先级任务被阻塞或延迟执行的现象。

这种情况通常发生在多任务系统中,其中任务的优先级不同,且存在共享资源。当一个低优先级任务获得了一个高优先级任务所需的资源(如互斥量、信号量等)并持有该资源时,如果此时另一个高优先级任务处于就绪态,需要访问该资源,那么该高优先级任务将被阻塞,等待低优先级任务释放资源。这样,本应优先执行的高优先级任务却被低优先级任务所阻塞,导致优先级翻转。

优先级翻转可能会导致系统性能下降,因为高优先级任务被阻塞时,系统无法及时响应高优先级任务的请求,从而影响了系统的实时性和响应性能。为了解决优先级翻转问题,通常采取以下几种方法:

  1. 优先级继承(Priority Inheritance):当一个低优先级任务持有了一个高优先级任务所需的资源时,系统会将低优先级任务的优先级提升到需要访问该资源的最高优先级任务的优先级,直到低优先级任务释放资源。这样可以避免高优先级任务被阻塞的情况。

  2. 优先级屏蔽(Priority Ceiling):给每个共享资源定义一个优先级屏蔽值,当一个任务获取了资源时,系统将其优先级提升到该资源的优先级屏蔽值,直到释放资源。这样可以确保在任何时候只有一个任务可以访问资源,并且高优先级任务不会被阻塞。

  3. 不可抢占(Non-preemptive):在一些实时操作系统中,可以采用不可抢占的策略,即任务只有在主动放弃CPU时才会被调度让出,而不会因为优先级低而被其他任务抢占。这样可以避免因优先级翻转而导致的问题。

三、互斥量的函数使用

1.创建互斥量

这两个函数是用于创建和初始化互斥量的函数,它们在RT-Thread中用来管理任务之间的互斥访问。

rt_mutex_create(const char* name, rt_uint8_t flag):

参数:
name: 互斥量的名称,用于标识互斥量。
flag: 互斥量的标志位,可以选择是否自动获取。当设置为RT_IPC_FLAG_PRIO时,表示互斥量的优先级继承特性,即互斥量持有者的优先级将会被提升到等待此互斥量的最高优先级。

返回值:
如果成功创建互斥量,则返回一个指向互斥量对象的句柄。
如果创建失败,则返回RT_NULL。

rt_err_t rt_mutex_init(rt_mutex_t mutex, const char* name, rt_uint8_t flag):

参数:
mutex: 互斥量句柄,即通过rt_mutex_create创建的互斥量对象。
name: 互斥量的名称,用于标识互斥量。
flag: 互斥量的标志位,可以选择是否自动获取。当设置为RT_IPC_FLAG_PRIO时,表示互斥量的优先级继承特性,即互斥量持有者的优先级将会被提升到等待此互斥量的最高优先级。

返回值:
如果成功初始化互斥量,则返回RT_EOK。
如果初始化失败,则返回相应的错误码,比如RT_ERROR或RT_ENOMEM等。

这两个函数的作用是创建和初始化互斥量,其中rt_mutex_create函数可以用于创建一个新的互斥量对象,并返回一个句柄;而rt_mutex_init函数则是对已经存在的互斥量进行初始化,使之具备可用状态。

2.删除互斥量

这两个函数是用于删除和分离互斥量的函数,它们在RT-Thread中用于释放互斥量资源,以及取消互斥量与任务之间的关联。

1.rt_err_t rt_mutex_delete(rt_mutex_t mutex):

  • 参数:

    • mutex: 互斥量句柄,即通过rt_mutex_create创建的互斥量对象。
  • 返回值:

    • 如果成功删除互斥量,则返回RT_EOK
    • 如果删除失败,则返回相应的错误码,比如RT_ERROR等。
  • 功能:

    • 该函数用于删除一个已经存在的互斥量对象,释放相应的资源,并且取消与此互斥量相关联的任务关系。
  1. rt_err_t rt_mutex_detach(rt_mutex_t mutex):
    • 参数:

      • mutex: 互斥量句柄,即通过rt_mutex_create创建的互斥量对象。
    • 返回值:

      • 如果成功分离互斥量,则返回RT_EOK
      • 如果分离失败,则返回相应的错误码,比如RT_ERROR等。
    • 功能:

      • 该函数用于分离一个已经存在的互斥量对象,取消与此互斥量相关联的任务关系,但不会释放互斥量资源,互斥量本身仍然存在。

这两个函数的作用是用于释放互斥量资源和取消互斥量与任务之间的关联。rt_mutex_delete函数会删除互斥量对象并释放相关资源,而rt_mutex_detach函数则只是取消与互斥量相关联的任务关系,但不释放互斥量资源。

3.获取互斥量

这两个函数都用于获取互斥量的访问权限,但在不同的情况下有不同的行为:

  1. rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time):

    • 参数:
      • mutex: 互斥量句柄,即通过rt_mutex_create创建的互斥量对象。
      • time: 等待互斥量的超时时间,单位为系统时钟节拍。如果设置为RT_WAITING_FOREVER,则表示无限等待;如果设置为RT_WAITING_NO,表示不等待。
    • 返回值:
      • 如果成功获取互斥量的访问权限,则返回RT_EOK
      • 如果等待超时,则返回RT_ETIMEOUT
      • 如果出现错误,则返回相应的错误码,比如RT_ERROR等。
    • 功能:
      • 该函数尝试获取互斥量的访问权限,如果互斥量当前被其他任务占用,则当前任务会被阻塞,直到互斥量被释放或超时。
      • 如果设置了超时时间,任务会在超时时间内等待,超时后函数返回超时错误码。
      • 如果不设置超时时间,则任务会一直等待直到互斥量被释放。
  2. rt_err_t rt_mutex_trytake(rt_mutex_t mutex):

    • 参数:
      • mutex: 互斥量句柄,即通过rt_mutex_create创建的互斥量对象。
    • 返回值:
      • 如果成功获取互斥量的访问权限,则返回RT_EOK
      • 如果互斥量已被其他任务占用,则返回RT_EBUSY
      • 如果出现其他错误,则返回相应的错误码,比如RT_ERROR等。
    • 功能:
      • 该函数尝试获取互斥量的访问权限,与rt_mutex_take不同的是,如果互斥量当前被其他任务占用,当前任务不会被阻塞,而是立即返回获取失败的错误码。

这两个函数允许任务在使用临界资源时进行同步,确保只有一个任务可以访问关键资源。rt_mutex_take函数会在获取互斥量失败时进行等待,而rt_mutex_trytake函数则会立即返回失败,不进行等待。

4.释放互斥量

rt_err_t rt_mutex_release(rt_mutex_t mutex) 函数用于释放由互斥量控制的临界资源,让其他任务可以访问该资源。具体讲解如下:

  • 参数

    • mutex:互斥量句柄,即通过 rt_mutex_create 创建的互斥量对象。
  • 返回值

    • 如果成功释放互斥量的访问权限,则返回 RT_EOK
    • 如果出现错误,则返回相应的错误码,比如 RT_ERROR 等。
  • 功能

    • 该函数用于释放任务持有的互斥量,使其他任务可以获取该互斥量的访问权限。
    • 一般情况下,只有成功获取了互斥量的任务才能调用该函数进行释放。
    • 释放互斥量后,其他等待该互斥量的任务将会根据其优先级重新竞争该资源的访问权限。

四、互斥量的基本使用

#include <rtthread.h>

// 定义一个互斥量对象
static struct rt_mutex mutex;

// 定义一个线程函数,用于演示互斥量的使用
static void thread_entry(void *parameter)
{
    rt_err_t result;

    // 获取互斥量
    result = rt_mutex_take(&mutex, RT_WAITING_FOREVER);
    if (result == RT_EOK)
    {
        rt_kprintf("Thread %s acquired mutex successfully!\n", rt_thread_self()->name);
        // 在这里可以执行临界区代码,操作共享资源
        rt_thread_mdelay(1000); // 模拟对共享资源的访问
        // 释放互斥量
        rt_mutex_release(&mutex);
        rt_kprintf("Thread %s released mutex successfully!\n", rt_thread_self()->name);
    }
    else
    {
        rt_kprintf("Thread %s failed to acquire mutex!\n", rt_thread_self()->name);
    }
}

// 创建一个互斥量的示例
static int mutex_example_init(void)
{
    rt_err_t result;

    // 初始化互斥量
    result = rt_mutex_init(&mutex, "mutex", RT_IPC_FLAG_PRIO);
    if (result != RT_EOK)
    {
        rt_kprintf("Failed to initialize mutex! Error code: %d\n", result);
        return -1;
    }

    // 创建线程,用于演示互斥量的使用
    rt_thread_t tid = rt_thread_create("thread", thread_entry, RT_NULL, 1024, 25, 10);
    if (tid != RT_NULL)
    {
        rt_thread_startup(tid);
    }
    else
    {
        rt_kprintf("Failed to create thread!\n");
        return -1;
    }

    return 0;
}
// 初始化线程
INIT_APP_EXPORT(mutex_example_init);

五、FreeRTOS中互斥量和RT-Thread互斥量的区别

FreeRTOS中的互斥量:

在FreeRTOS中,互斥量是通过函数 xSemaphoreTake() 和 xSemaphoreGive() 来获取和释放的。
与任务关联,任务可以获取和释放互斥量。
没有明确的谁上锁谁解锁的概念。任务可以获取任何可用的互斥量,并且可以在不同的任务之间传递互斥量。

RT-Thread中的互斥量:

在RT-Thread中,互斥量是通过 rt_mutex_take() 和 rt_mutex_release() 函数获取和释放的。
互斥量与任务关联,任务可以获取和释放它们。
实现了“谁上锁谁解锁”的功能。这意味着只有成功获取互斥量的任务才能释放它,从而确保了互斥量的所有权。
因此,RT-Thread的互斥量实现了更严格的所有权模式,

确保了只有成功获取互斥量的任务才能释放它,从而避免了可能的资源竞争和数据破坏。这种设计可以更好地管理任务之间的资源访问,提高系统的可靠性和稳定性。

总结

本篇文章主要讲解了互斥量的基本概念和基本使用,还对比了互斥量和信号量的区别,以及讲解了什么是优先级翻转,以及解决方法,并且讲解了FreeRTOS中互斥量和RT-Thread互斥量的区别。