互斥量是一种特殊的二值信号量。互斥量的状态只有两种,开锁或闭锁(两种状态值)。
互斥量支持递归,持有该互斥量的线程也能够再次获得这个锁而不被挂起。自己能够再次获得互斥量。
互斥量可以解决优先级翻转问题,它能够实现优先级继承
优先级翻转:
在很多场合中,某些资源只有一个,当低优先级任务正在占用该资源时,即便高优先级任务也只能等待低优先级任务使用该资源后释放资源,这里高优先级任务无法运行而低优先级任务可以运行的现象称为优先级翻转。低优先级任务获取信号量后,被中优先级打断,中优先级执行时间较长,因为低优先级任务还未释放信号量,高优先级任务就无法获取信号量继续运行。
使用信号量会导致的另一个潜在问题是线程优先级翻转问题。所谓优先级翻转,即当一个高优先级线程试图通过信号量机制访问共享资源时,如果该信号量已被一低优先级线程持有,而这个低优先级线程在运行过程中可能又被其它一些中等优先级的线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞,实时性难以得到保证。如下图所示:有优先级为 A、B 和 C 的三个线程,优先级 A> B > C。线程 A,B 处于挂起状态,等待某一事件触发,线程 C 正在运行,此时线程 C 开始使用某一共享资源 M。在使用过程中,线程 A 等待的事件到来,线程 A 转为就绪态,因为它比线程 C 优先级高,所以立即执行。但是当线程 A 要使用共享资源 M 时,由于其正在被线程 C 使用,因此线程 A 被挂起切换到线程 C 运行。如果此时线程 B 等待的事件到来,则线程 B 转为就绪态。由于线程 B 的优先级比线程 C 高,且线程B没有用到共享资源 M ,因此线程 B 开始运行,直到其运行完毕,线程 C 才开始运行。只有当线程 C 释放共享资源 M 后,线程 A 才得以执行。在这种情况下,优先级发生了翻转:线程 B 先于线程 A 运行。这样便不能保证高优先级线程的响应时间
优先级继承:
在 RT-Thread 操作系统中,互斥量可以解决优先级翻转问题,实现的是优先级继承协议 (Sha, 1990)。优先级继承是通过在线程 A 尝试获取共享资源而被挂起的期间内,将线程 C 的优先级提升到线程 A 的优先级别,从而解决优先级翻转引起的问题。这样能够防止 C(间接地防止 A)被 B 抢占,如下图所示。优先级继承是指,提高某个占有某种资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,而当这个低优先级线程释放该资源时,优先级重新回到初始设定。因此,继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。
注:在获得互斥量后,请尽快释放互斥量,不得再行更改持有互斥量的优先级,否则可能认为引入无界优先级反转的问题
需要切记的是互斥量不能在中断服务历程中使用
互斥量操作:
1、创建互斥量:
/**
参数的含义:
1、name 互斥量名称
2、flag 该标志已经作废,无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO,
内核均按照 RT_IPC_FLAG_PRIO 处理
返回值:
互斥量创建成功,返回互斥量的控制块指针
互斥量创建失败,返回RT_BULL
*/
rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)
2、删除互斥量
/**
参数:
mutex 互斥量对象的句柄
返回值:
RT_EOK 删除成
*/
rt_err_t rt_mutex_delete(rt_mutex_t mutex)
3、初始化互斥量
/**
参数的含义:
1、mutex 互斥量对象的句柄,指向互斥量对象的内存块,开始定义的结构体
2、name 互斥量名称
3、flag 该标志已经作废,按照 RT_IPC_FLAG_PRIO (优先级)处理
返回值:
RT_EOK 初始化成功
*/
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)
4、脱离互斥量
/**
参数:
mutex 互斥量对象的句柄
返回值:
RT_EOK 成功
*/
rt_err_t rt_mutex_detach(rt_mutex_t mutex)
5、获取互斥量
一个时刻一个互斥量只能被一个线程持有。
如果互斥量没有被其他线程控制,那么申请该互斥量的线程将成功获得该互斥量。如果互斥量已经被当前线程线程控制,则该互斥量的持有计数加 1,当前线程也不会挂起等待
/**
参数:
1、mutex 互斥量对象的句柄
2、time 指定的等待时间,单位是操作系统时钟节拍(OS Tick)
返回值:
RT_EOK 成功获得互斥量
-RT_ETIMEOUT 超时依然未获得互斥量
-RT_ERROR 获取失败
*/
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)
6、释放互斥量
/**
参数:
mutex 互斥量对象的句
返回值:
RT_EOK 成功
*/
rt_err_t rt_mutex_release(rt_mutex_t mutex)
7、代码
#include <rtthread.h>
#define THREAD_PRIORITY 8
#define THREAD_TIMESLICE 5
/* 指向互斥量的指针 */
static rt_mutex_t dynamic_mutex = RT_NULL;
static rt_uint8_t number1,number2 = 0;
ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
static void rt_thread_entry1(void *parameter)
{
while(1)
{
/* 线程1获取到互斥量后,先后对number1、number2进行加1操作,然后释放互斥量 */
rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
number1++;
rt_thread_mdelay(10);
number2++;
rt_mutex_release(dynamic_mutex);
}
}
ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
static void rt_thread_entry2(void *parameter)
{
while(1)
{
/* 线程2获取到互斥量后,检查number1、number2的值是否相同,相同则表示mutex起到了锁的作用 */
rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
if(number1 != number2)
{
rt_kprintf("not protect.number1 = %d, number2 = %d \n",number1 ,number2);
}
else
{
rt_kprintf("mutex protect ,number1 = number2 is %d\n",number1);
}
number1++;
number2++;
rt_mutex_release(dynamic_mutex);
if(number1 >=50)
return;
}
}
/* 互斥量示例的初始化 */
int mutex_sample(void)
{
/* 创建一个动态互斥量 */
dynamic_mutex = rt_mutex_create("dmutex", RT_IPC_FLAG_FIFO);
if (dynamic_mutex == RT_NULL)
{
rt_kprintf("create dynamic mutex failed.\n");
return -1;
}
rt_thread_init(&thread1,
"thread1",
rt_thread_entry1,
RT_NULL,
&thread1_stack[0],
sizeof(thread1_stack),
THREAD_PRIORITY, THREAD_TIMESLICE);
rt_thread_startup(&thread1);
rt_thread_init(&thread2,
"thread2",
rt_thread_entry2,
RT_NULL,
&thread2_stack[0],
sizeof(thread2_stack),
THREAD_PRIORITY-1, THREAD_TIMESLICE);
rt_thread_startup(&thread2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mutex_sample, mutex sample);
8、仿真输出