4.8 KiB
Raw Blame History

(_mutex_doc)=

:::{note} 作者:龙进 longjin@RinGoTek.cn :::

mutex互斥量

mutex是一种轻量级的同步原语只有被加锁、空闲两种状态。

当mutex被占用时尝试对mutex进行加锁操作的进程将会被休眠直到资源可用。

1. 特性

  • 同一时间只有1个任务可以持有mutex
  • 不允许递归地加锁、解锁
  • 只允许通过mutex的api来操作mutex
  • 在硬中断、软中断中不能使用mutex

2. 定义

mutex定义在lib/mutex.rs中,定义如下所示:

/// @brief Mutex互斥量结构体
/// 请注意由于Mutex属于休眠锁因此如果您的代码可能在中断上下文内执行请勿采用Mutex
#[derive(Debug)]
pub struct Mutex<T> {
    /// 该Mutex保护的数据
    data: UnsafeCell<T>,
    /// Mutex内部的信息
    inner: SpinLock<MutexInner>,
}

#[derive(Debug)]
struct MutexInner {
    /// 当前Mutex是否已经被上锁(上锁时为true)
    is_locked: bool,
    /// 等待获得这个锁的进程的链表
    wait_list: LinkedList<&'static mut process_control_block>,
}

3. 使用

与SpinLock类似Rust版本的Mutex具有一个守卫。使用的时候需要将要被保护的数据的所有权移交Mutex。并且守卫只能在加锁成功后产生因此每个时刻每个Mutex最多存在1个守卫。

当需要读取、修改Mutex保护的数据时请先使用Mutex的lock()方法。该方法会返回一个MutexGuard。您可以使用被保护的数据的成员函数来进行一些操作。或者是直接读取、写入被保护的数据。(相当于您获得了被保护的数据的可变引用)

  完整示例如下方代码所示:

let x :Mutex<Vec<i32>>= Mutex::new(Vec::new());
    {
        let mut g :MutexGuard<Vec<i32>>= x.lock();
        g.push(1);
        g.push(2);
        g.push(2);
        assert!(g.as_slice() == [1, 2, 2] || g.as_slice() == [2, 2, 1]);
        // 在此处Mutex是加锁的状态
        debug!("x={:?}", x);
    }
    // 由于上方的变量`g`也就是Mutex守卫的生命周期结束自动释放了Mutex。因此在此处Mutex是放锁的状态
    debug!("x={:?}", x);

对于结构体内部的变量我们可以使用Mutex进行细粒度的加锁也就是使用Mutex包裹需要细致加锁的成员变量比如这样

pub struct a {
  pub data: Mutex<data_struct>,
}

  当然,我们也可以对整个结构体进行加锁:

struct MyStruct {
  pub data: data_struct,
}
/// 被全局加锁的结构体
pub struct LockedMyStruct(Mutex<MyStruct>);

4. API

4.1. new - 初始化Mutex

原型

pub const fn new(value: T) -> Self

说明

new()方法用于初始化一个Mutex。该方法需要一个被保护的数据作为参数。并且该方法会返回一个Mutex。

4.2. lock - 加锁

原型

pub fn lock(&self) -> MutexGuard<T>

说明

对Mutex加锁返回Mutex的守卫您可以使用这个守卫来操作被保护的数据。

如果Mutex已经被加锁那么该方法会阻塞当前进程直到Mutex被释放。

4.3. try_lock - 尝试加锁

原型

pub fn try_lock(&self) -> Result<MutexGuard<T>, i32>

说明

尝试对Mutex加锁。如果加锁失败不会将当前进程加入等待队列。如果加锁成功返回Mutex的守卫如果当前Mutex已经被加锁返回Err(错误码)

5. C版本的Mutex在将来会被废弃

mutex定义在common/mutex.h中。其数据类型如下所示:

typedef struct
{

    atomic_t count; // 锁计数。1->已解锁。 0->已上锁,且有可能存在等待者
    spinlock_t wait_lock;   // mutex操作锁用于对mutex的list的操作进行加锁
    struct List wait_list;  // Mutex的等待队列
} mutex_t;

5.1. API

mutex_init

void mutex_init(mutex_t *lock)

初始化一个mutex对象。

mutex_lock

void mutex_lock(mutex_t *lock)

对一个mutex对象加锁。若mutex当前被其他进程持有则当前进程进入休眠状态。

mutex_unlock

void mutex_unlock(mutex_t *lock)

对一个mutex对象解锁。若mutex的等待队列中有其他的进程则唤醒下一个进程。

mutex_trylock

void mutex_trylock(mutex_t *lock)

尝试对一个mutex对象加锁。若mutex当前被其他进程持有则返回0.否则加锁成功返回1.

mutex_is_locked

void mutex_is_locked(mutex_t *lock)

判断mutex是否已被加锁。若给定的mutex已处于上锁状态则返回1否则返回0。