互斥锁,IPC的一部分。互斥锁,算是信号量中特殊的一个例子,即信号量的数量只为1,只要有任何一个线程获取到这个互斥量,在没有释放这个互斥量之前,任何线程都获取不到。除此之外,互斥锁还有一个作用,那就是提升当前信号量获取线程的的优先级。
/*节选自rt_mutex_take()函数*/
/* change the owner thread priority of mutex */
if (thread->current_priority < mutex->owner->current_priority)
{
/* change the owner thread priority */
rt_thread_control(mutex->owner,
RT_THREAD_CTRL_CHANGE_PRIORITY,
&thread->current_priority);
}
除此之外,我还要申明一下rt_thread_delay()挂起函数,我之前一直是认为,在rt_thread_delay延时函数的时间到达后,将挂起态取消。如果很不巧这个时间,恰巧是CPU正在执行其他工作,那么这个延时将实际被延长;如果运气差到爆棚,将永远不能被执行。程序之所以能运行,是因为CPU大多都是在空闲状态。现在仍然是没有太仔细阅读源码,不能做出细致的分析,但是我的确是堆实时操作系统有了新的理解,这个部分在内核编程学习内容结束后再发出。今天的主要内容仍然是互斥锁
1.互斥锁的创建
互斥锁与信号量类似,同样是有两种创建方式
/*静态创建*/
static struct rt_mutex static_mutex;
rt_mutex_init(&static_mutex,"name",type);
/*动态创建*/
static rt_mutex_t dynamic_mutex = RT_NULL;
dynamic_mutex = rt_mutex_creat("name",type);
/*type是指类型,是先进先出的堆模式,还是伴随优先级模式*/
//常用 RT_IPC_FLAG_FIFO RT_IPC_FLAG_PRIO
2.互斥锁的获取
/*信号量的获取*/
rt_mutex_take(&static_mutex,time);
rt_mutex_take(dynamic_mutex,time);
/*time指等待时间,支持永久等待*/
// RT_WAITING_FOREVER
3.互斥锁的释放
rt_mutex_release(&static_mutex);
rt_mutex_release(dynamic_mutex);
4.例程,例程内容不包含静态创建,以后增加
这个例程充分展现了互斥锁的保护作用,在未获得互斥锁之前,一直处于等待状态等待获取互斥锁。获取,释放后,都将启动任务调度。有下列例程来显示:
static void rt_thread_entry1(void *parameter)
{
while(1)
{
/* 线程1获取到互斥量后,先后对number1、number2进行加1操作,然后释放互斥量 */
rt_kprintf("thread1 has been starting\n");
rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
rt_kprintf("thread1 has got mutex\n");
number1++;
rt_thread_mdelay(10);
number2++;
rt_kprintf("thread1 release mutex\n");
rt_mutex_release(dynamic_mutex);
if(number1 >=100)
return;
}
}
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_kprintf("thread2 has been starting\n");
rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
rt_kprintf("thread2 has got mutex\n");
if(number1 != number2)
{
rt_kprintf("not protect.number1 = %d, mumber2 = %d \n",number1 ,number2);
}
else
{
rt_kprintf("mutex protect ,number1 = mumber2 is %d\n",number1);
}
number1++;
number2++;
rt_kprintf("thread2 release mutex\n");
rt_mutex_release(dynamic_mutex);
if(number1 >=100)
return;
}
}
/*打印结果:*/
/*
thread1 has been starting
thread1 has got mutex
thread2 has been starting
thread1 release mutex
thread1 has been starting
thread2 has got mutex
mutex protect ,number1 = mumber2 is 1
thread2 release mutex
thread2 has been starting
thread1 has got mutex
thread1 release mutex
thread1 has been starting
thread2 has got mutex
mutex protect ,number1 = mumber2 is 3
thread2 release mutex
thread2 has been starting
thread1 has got mutex
thread1 release mutex
thread1 has been starting
thread2 has got mutex
mutex protect ,number1 = mumber2 is 5
thread2 release mutex
thread2 has been starting
thread1 has got mutex
thread1 release mutex
thread1 has been starting
thread2 has got mutex
mutex protect ,number1 = mumber2 is 7
*/
信号量的获取,释放都会启动任务调度。牢记这点分析程序。
/**
* This function will release a mutex, if there are threads suspended on mutex,
* it will be waked up.
*
* @param mutex the mutex object
*
* @return the error code
*/
rt_err_t rt_mutex_release(rt_mutex_t mutex)
{
register rt_base_t temp;
struct rt_thread *thread;
rt_bool_t need_schedule;
//省略一段
need_schedule = RT_FALSE;
//省略一段
if (mutex->hold == 0)
{
//省略一段
/* wakeup suspended thread */
if (!rt_list_isempty(&mutex->parent.suspend_thread))
{
/* get suspended thread */
thread = rt_list_entry(mutex->parent.suspend_thread.next,
struct rt_thread,
tlist);
RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_release: resume thread: %s\n",
thread->name));
/* set new owner and priority */
mutex->owner = thread;
mutex->original_priority = thread->current_priority;
mutex->hold ++;
/* resume thread */
rt_ipc_list_resume(&(mutex->parent.suspend_thread));
need_schedule = RT_TRUE;
}
//省略一段
}
//省略一段
/* perform a schedule */
if (need_schedule == RT_TRUE)
rt_schedule();
//省略一段
}
具体得就不写了,大家可以参靠源码分析。
5.例程,互斥锁获取者与等待持有者的优先级同化
我也是机缘巧合才看到这一段代码的,以前从来没有考虑过这种问题。现在也没有搞清为什么需要提高获取者的线程优先级到持有者的同一水平,那有需要时候再回来添加,先放在这里占一个位置。
下面是代码内容,打印信息,大家可以拿回去自己打印。
/*
* 程序清单:互斥量使用例程
*
* 这个例子将创建 3 个动态线程以检查持有互斥量时,持有的线程优先级是否
* 被调整到等待线程优先级中的最高优先级。
*
* 线程 1,2,3 的优先级从高到低分别被创建,
* 线程 3 先持有互斥量,而后线程 2 试图持有互斥量,此时线程 3 的优先级应该
* 被提升为和线程 2 的优先级相同。线程 1 用于检查线程 3 的优先级是否被提升
* 为与线程 2的优先级相同。
*/
#include <rtthread.h>
/* 指向线程控制块的指针 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
static rt_thread_t tid3 = RT_NULL;
static rt_mutex_t mutex = RT_NULL;
#define THREAD_PRIORITY 10
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
/* 线程 1 入口 */
static void thread1_entry(void *parameter)
{
while(1)
{
/* 先让低优先级线程运行 */
rt_thread_mdelay(100);
/* 此时 thread3 持有 mutex,并且 thread2 等待持有 mutex */
/* 检查 rt_kprintf("the producer generates a number: %d\n", array[set%MAXSEM]); 与 thread3 的优先级情况 */
if (tid2->current_priority != tid3->current_priority)
{
/* 优先级不相同,测试失败 */
rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
rt_kprintf("test failed.\n");
return;
}
else
{
rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
rt_kprintf("test OK.\n");
}
}
}
/* 线程 2 入口 */
static void thread2_entry(void *parameter)
{
rt_err_t result;
rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
/* 先让低优先级线程运行 */
rt_thread_mdelay(50);
rt_kprintf("Get thread2\n");
/*
* 试图持有互斥锁,此时 thread3 持有,应把 thread3 的优先级提升
* 到 thread2 相同的优先级
*/
result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
rt_kprintf("thread2 has got mutex\n");
if (result == RT_EOK)
{
/* 释放互斥锁 */
rt_kprintf("release mutex\n");
rt_mutex_release(mutex);
}
}
/* 线程 3 入口 */
static void thread3_entry(void *parameter)
{
rt_tick_t tick;
rt_err_t result;
rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
rt_kprintf("Get thread3\n");
result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
rt_kprintf("thread3 has got mutex\n");
if (result != RT_EOK)
{
rt_kprintf("thread3 take a mutex, failed.\n");
}
/* 做一个长时间的循环,500ms */
tick = rt_tick_get();
while (rt_tick_get() - tick < (RT_TICK_PER_SECOND / 2)) ;
rt_kprintf("Leave thread3\n");
rt_mutex_release(mutex);
}
int pri_inversion(void)
{
/* 创建互斥锁 */
mutex = rt_mutex_create("mutex", RT_IPC_FLAG_FIFO);
if (mutex == RT_NULL)
{
rt_kprintf("create dynamic mutex failed.\n");
return -1;
}
/* 创建线程 1 */
tid1 = rt_thread_create("thread1",
thread1_entry,
RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY-1, THREAD_TIMESLICE);
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
/* 创建线程 2 */
tid2 = rt_thread_create("thread2",
thread2_entry,
RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid2 != RT_NULL)
rt_thread_startup(tid2);
/* 创建线程 3 */
tid3 = rt_thread_create("thread3",
thread3_entry,
RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY + 1, THREAD_TIMESLICE);
if (tid3 != RT_NULL)
rt_thread_startup(tid3);
return 0;
}
/*打印输出
priority of thread2 is: 10
the priority of thread3 is: 11
Get thread3
thread3 has got mutex
Get thread2
the priority of thread2 is: 10
the priority of thread3 is: 10
test OK.
the priority of thread2 is: 10
the priority of thread3 is: 10
test OK.
the priority of thread2 is: 10
the priority of thread3 is: 10
test OK.
the priority of thread2 is: 10
the priority of thread3 is: 10
test OK.
the priority of thread2 is: 10
the priority of thread3 is: 10
test OK.
Leave thread3
thread2 has got mutex
release mutex
the priority of thread2 is: 10
the priority of thread3 is: 11
test failed. //这里不是属于测试失败,因为当前互斥锁持有者并不是线程3,不应该提升线程优先级
*/
这里还有一个很有意思的语句
/* 做一个长时间的循环,500ms */
tick = rt_tick_get();
while (rt_tick_get() - tick < (RT_TICK_PER_SECOND / 2)) ;
长延时,并不是利用dealy函数,所以不会主动出让CPU使用权。不能主动出让使用权,那500ms的长延时,貌似并没有到达那么久。如果是那么久的话,不会是这个打印结果。所以也侧面验证,在线程1,线程2的rt_thread_delay()函数到达延时时间时,系统启动了调度,夺走了CPU使用权。幸好CPU现在只是在做没有太大意义的延时操作,如果是正在有关时序操作的话,那么读写一定是失败的。