互斥量又叫相互排斥的信号量,是一种特殊的二值信号量。互斥量类似于只有一个车位的停车场:当有一辆车进入的时候,将停车场大门锁住,其他车辆在外面等候。当里面的车出来时,将停车场大门打开,下一辆车才可以进入。
互斥量工作机制
- 拥有互斥量的进程拥有互斥量的所有权
- 互斥量支持递归访问且能防止线程优先级翻转
- 互斥量只能由持有线程释放,信号量则可以由任何线程释放
- 互斥量的状态只有两种,开锁或闭锁,当线程持有这个互斥量时,互斥量闭锁,线程释放互斥量时,互斥量开锁
- 互斥量如何解决优先级翻转,优先级翻转是指优先级高的线程由于某些原因被挂起,而优先级低的线程先被执行,而互斥量解决优先级翻转的措施是再优先级低的线程使用互斥量时,互斥量有一个优先级继承协议,这个协议会把当前使用用互斥量的线程的优先级提高到较高级别,从而使得优先级比它高的线程无法抢占它,而其他优先级高且需要使用互斥量的线程也不会被很多优先级较低的线程阻塞
- 在获得互斥量后,请尽快释放互斥量,并且在持有互斥量的过程中,不得再行更改持有互斥量线程的优先级,否则可能人为引入无界优先级反转的问题。
互斥量控制块
struct rt_mutex
{
struct rt_ipc_object parent; /* 继承自 ipc_object 类 */
rt_uint16_t value; /* 互斥量的值 */
rt_uint8_t original_priority; /* 持有线程的原始优先级 */
rt_uint8_t hold; /* 持有线程的持有次数 */
struct rt_thread *owner; /* 当前拥有互斥量的线程 */
};
/* rt_mutext_t 为指向互斥量结构体的指针类型 */
typedef struct rt_mutex* rt_mutex_t;
和信号量一样,rt_mutex也是有IPC容器管理的
互斥量操作
1.互斥量的创建和删除
创建一个互斥量时,首先要创建一个互斥量控制块,然后完成对该控制块的初始化工作,创建互斥量的函数如下:
rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);
name 为互斥量名称,flag只能为RT_IPC_FLAG_PRIO 或者RT_IPC_FLAG_FIFO,但内核均按照RT_IPC_FLAG_PRIO处理
但不在使用互斥量时可以有持有线程来通过相应接口删除
rt_err_t rt_mutex_delete (rt_mutex_t mutex);
mutex为互斥量句柄
当内核删除互斥量时,所有等待该互斥量的线程都将被唤醒,等待线程获得的返回值为-RT_ERROR,然后该互斥量从内核管理器链表中删除,并释放空间
2.互斥量的初始化和脱离
用户也可以使用初始化函数来创建静态互斥量,函数如下:
rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag);
mutex为句柄,name为互斥量名称,flag和创建函数相同,都只能选择RT_IPC_FLAG_PRIO
使用初始化函数来获取一个互斥量时,必须使用脱离函数来删除这个互斥量:
rt_err_t rt_mutex_detach (rt_mutex_t mutex);
3.互斥量的获取
如果互斥量没有被其他线程获取时,则任何线程都可以使用如下函数来获取互斥量:
rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);
如果互斥量已经被线程拥有,那么其他线程调用该函数时,其他线程就会再该互斥量上挂起等待,知道持有线程释放互斥量,或者超过等待时间
mutex时需要获取的互斥量的句柄,time就是等待时间
当用户不想在申请的互斥量上挂起线程进行等待时,可以使用无等待方式获取互斥量,无等待获取互斥量使用下面的函数接口:
rt_err_t rt_mutex_trytake(rt_mutex_t mutex);
4.释放互斥量
当线程完成互斥资源的访问后,应尽快释放它占据的互斥量,使得其他线程能及时获取该互斥量。释放互斥量使用下面的函数接口:
rt_err_t rt_mutex_release(rt_mutex_t mutex);
该函数接口只有持有互斥量的线程才可以调用,持有线程没调用一次该函数,互斥量的持有计数就会减一,当互斥量的持有计数为0时,互斥量就会变为可用