本文简要说明Zephyr互斥量的使用和实现。
在Zephyr内核对象-同步对象简介一文中已经大概介绍了mutex的特性,本文将继续说明互斥量的使用和实现。
使用
API
#define K_MUTEX_DEFINE(name)
作用:定义一个k_mutex,并初始化
name: k_mutex name
int k_mutex_init(struct k_mutex *mutex)
作用: 初始化mutex
mutex: 要初始化的mutex
返回值: 0标示初始化成功
int k_mutex_lock(struct k_mutex *mutex, s32_t timeout)
作用:加锁
mutex: 加锁的互斥量
timeout: 等待时间,单位ms。K_NO_WAIT不等待, K_FOREVER一直等
返回值: 0表示加锁成功
int k_mutex_unlock(struct k_mutex *mutex)
作用:解锁
mutex:解锁的互斥量
返回值: 0表示解锁成功,-EPERM表示当前thread并不拥有这个互斥量, -EINVAL表示该互斥量没有被锁
使用说明
使用互斥量完成对资源的独占访问。
Mutex不能在ISR内使用。
初始化
先初始化一个互斥量,下面两种方式的效果是一样的
方法1,使用宏
1 | K_MUTEX_DEFINE(my_mutex); |
方法2,使用函数
1 | struct k_mutex my_mutex; |
资源独占访问
下列示例代码中Thread A和B都要去访问一个IO资源,但同一时间IO只能被独占访问,因此使用互斥量包含
1 | thread_A() |
实现
k_mutex结构体如下,可以看出其基本实现是用的wait_q
1 | struct k_mutex { |
在Zephyr内核对象-同步对象简介一文中只说了优先级倒置,没有说可重入锁,这里简单的介绍一下,可以重入锁是指如果一个线程已经拥有了互斥量,那么该线程可以继续多次对该互斥量加锁,同时也要做对应次数的解锁,才能完全释放该互斥量
初始化
k_mutex_init->z_impl_k_mutex_init,详细分析见注释
1 | int z_impl_k_mutex_init(struct k_mutex *mutex) |
用宏也可以进行初始化
1 | #define _K_MUTEX_INITIALIZER(obj) \ |
加锁
k_mutex_lock -> z_impl_k_mutex_unlock,会做下面几件事
1.如果互斥量没其它线程用,直接获得互斥量返回
2.如果互斥量是本线程在用,对可重入锁自加
3.如果互斥锁被其它线程用了,进行优先级倒置调整,等待其它线程解锁互斥量
3.如果超时内等到其它线程解锁互斥量,回去互斥量然后返回
4.如果等互斥量超时,则放弃等待,检查是否有其它线程还在等待,已等待线程的优先级重新计算要倒置的优先级,重设拥有互斥量线程的优先级
1 | int z_impl_k_mutex_lock(struct k_mutex *mutex, s32_t timeout) |
解锁
k_mutex_unlock->z_impl_k_mutex_unlock,做下面几件事
1.检查解锁者合法性
2.接触重入锁
3.恢复优先级倒置
4.等待锁的线程获取mutex
1 | int z_impl_k_mutex_unlock(struct k_mutex *mutex) |
参考
https://docs.zephyrproject.org/latest/reference/kernel/synchronization/mutexes.html