xref: /DragonOS/docs/kernel/locking/mutex.md (revision 676b8ef62e1a0a1e52d65b40c53c1636a2954040)
1(_mutex_doc)=
2
3:::{note}
4作者:龙进 <longjin@RinGoTek.cn>
5:::
6
7# mutex互斥量
8
9&emsp;&emsp;mutex是一种轻量级的同步原语,只有被加锁、空闲两种状态。
10
11&emsp;&emsp;当mutex被占用时,尝试对mutex进行加锁操作的进程将会被休眠,直到资源可用。
12
13## 1. 特性
14
15- 同一时间只有1个任务可以持有mutex
16- 不允许递归地加锁、解锁
17- 只允许通过mutex的api来操作mutex
18- 在硬中断、软中断中不能使用mutex
19
20## 2. 定义
21
22&emsp;&emsp;mutex定义在`lib/mutex.rs`中,定义如下所示:
23
24```rust
25/// @brief Mutex互斥量结构体
26/// 请注意!由于Mutex属于休眠锁,因此,如果您的代码可能在中断上下文内执行,请勿采用Mutex!
27#[derive(Debug)]
28pub struct Mutex<T> {
29    /// 该Mutex保护的数据
30    data: UnsafeCell<T>,
31    /// Mutex内部的信息
32    inner: SpinLock<MutexInner>,
33}
34
35#[derive(Debug)]
36struct MutexInner {
37    /// 当前Mutex是否已经被上锁(上锁时,为true)
38    is_locked: bool,
39    /// 等待获得这个锁的进程的链表
40    wait_list: LinkedList<&'static mut process_control_block>,
41}
42
43```
44
45## 3. 使用
46
47&emsp;&emsp;与SpinLock类似,Rust版本的Mutex具有一个守卫。使用的时候,需要将要被保护的数据的所有权移交Mutex。并且,守卫只能在加锁成功后产生,因此,每个时刻,每个Mutex最多存在1个守卫。
48
49&emsp;&emsp;当需要读取、修改Mutex保护的数据时,请先使用Mutex的`lock()`方法。该方法会返回一个`MutexGuard`。您可以使用被保护的数据的成员函数来进行一些操作。或者是直接读取、写入被保护的数据。(相当于您获得了被保护的数据的可变引用)
50
51&emsp;&emsp;完整示例如下方代码所示:
52
53```rust
54let x :Mutex<Vec<i32>>= Mutex::new(Vec::new());
55    {
56        let mut g :MutexGuard<Vec<i32>>= x.lock();
57        g.push(1);
58        g.push(2);
59        g.push(2);
60        assert!(g.as_slice() == [1, 2, 2] || g.as_slice() == [2, 2, 1]);
61        // 在此处,Mutex是加锁的状态
62        kdebug!("x={:?}", x);
63    }
64    // 由于上方的变量`g`,也就是Mutex守卫的生命周期结束,自动释放了Mutex。因此,在此处,Mutex是放锁的状态
65    kdebug!("x={:?}", x);
66```
67
68&emsp;&emsp;对于结构体内部的变量,我们可以使用Mutex进行细粒度的加锁,也就是使用Mutex包裹需要细致加锁的成员变量,比如这样:
69
70```rust
71pub struct a {
72  pub data: Mutex<data_struct>,
73}
74```
75
76&emsp;&emsp;当然,我们也可以对整个结构体进行加锁:
77
78```rust
79struct MyStruct {
80  pub data: data_struct,
81}
82/// 被全局加锁的结构体
83pub struct LockedMyStruct(Mutex<MyStruct>);
84```
85
86## 4. API
87
88### 4.1. new - 初始化Mutex
89
90#### 原型
91
92```rust
93pub const fn new(value: T) -> Self
94```
95
96#### 说明
97
98&emsp;&emsp;`new()`方法用于初始化一个Mutex。该方法需要一个被保护的数据作为参数。并且,该方法会返回一个Mutex。
99
100
101### 4.2. lock - 加锁
102
103#### 原型
104
105```rust
106pub fn lock(&self) -> MutexGuard<T>
107```
108
109#### 说明
110
111&emsp;&emsp;对Mutex加锁,返回Mutex的守卫,您可以使用这个守卫来操作被保护的数据。
112
113&emsp;&emsp;如果Mutex已经被加锁,那么,该方法会阻塞当前进程,直到Mutex被释放。
114
115### 4.3. try_lock - 尝试加锁
116
117#### 原型
118
119```rust
120pub fn try_lock(&self) -> Result<MutexGuard<T>, i32>
121```
122
123#### 说明
124
125&emsp;&emsp;尝试对Mutex加锁。如果加锁失败,不会将当前进程加入等待队列。如果加锁成功,返回Mutex的守卫;如果当前Mutex已经被加锁,返回`Err(错误码)`。
126
127## 5. C版本的Mutex(在将来会被废弃)
128
129&emsp;&emsp;mutex定义在`common/mutex.h`中。其数据类型如下所示:
130
131```c
132typedef struct
133{
134
135    atomic_t count; // 锁计数。1->已解锁。 0->已上锁,且有可能存在等待者
136    spinlock_t wait_lock;   // mutex操作锁,用于对mutex的list的操作进行加锁
137    struct List wait_list;  // Mutex的等待队列
138} mutex_t;
139```
140
141### 5.1. API
142
143#### mutex_init
144
145**`void mutex_init(mutex_t *lock)`**
146
147&emsp;&emsp;初始化一个mutex对象。
148
149#### mutex_lock
150
151**`void mutex_lock(mutex_t *lock)`**
152
153&emsp;&emsp;对一个mutex对象加锁。若mutex当前被其他进程持有,则当前进程进入休眠状态。
154
155#### mutex_unlock
156
157**`void mutex_unlock(mutex_t *lock)`**
158
159&emsp;&emsp;对一个mutex对象解锁。若mutex的等待队列中有其他的进程,则唤醒下一个进程。
160
161#### mutex_trylock
162
163**`void mutex_trylock(mutex_t *lock)`**
164
165&emsp;&emsp;尝试对一个mutex对象加锁。若mutex当前被其他进程持有,则返回0.否则,加锁成功,返回1.
166
167#### mutex_is_locked
168
169**`void mutex_is_locked(mutex_t *lock)`**
170
171&emsp;&emsp;判断mutex是否已被加锁。若给定的mutex已处于上锁状态,则返回1,否则返回0。
172