当rt-thread实时操作系统运行时,如果单纯使用信号量来管理公共资源,则会出现优先级翻转问题,导致高优先级线程被低优先级线程运行而阻塞,使得系统的实时性无法得到保证。下面为优先级翻转的具体解释
优先级翻转:
现有三个不同优先级的线程·,优先级分别为高,中,低,使用信号量机制访问同一公共资源(临界区)。
1.优先级为低的线程先占用信号量,访问公共资源,此时高优先级线程使用信号量访问临界区,由于低优先级线程已经占用信号量还未释放,则高优先级线程进入阻塞状态,等待信号量释放。
2.此时中优先级线程就绪,由于RT-Thread使用线程优先级抢占机制,则中优先级线程获得cpu使用权,低优先级线程挂起等待。
3.当中优先级线程运行完毕,让出cpu使用权,低优先级线程得以继续运行。当c优先级线程运行完毕,释放信号量,则高优先级线程获取信号量访问临界区,开始运行。
由此可见,此时高优先级线程没有立即获取cpu使用权,而是一直等待其他低优先级线程完成任务后释放信号量,才可运行,严重影响了系统的实时性。
为解决使用信号量优先级翻转问题,互斥量采用了优先级继承机制,下面为优先级继承机制的解释。
当有线程通过互斥量访问某一公共资源(临界区)时,此时若有多个线程访问此临界区,则进入线程等待队列,此时将占用临界区的线程优先级暂时调整为线程等待队列中优先级最高的线程相同优先级,则当外界即使有其他线程需要使用cpu时,也不会影响线程等待队列中的线程访问临界区,避免其他中等优先级线程抢占cpu,当此线程使用完毕释放互斥量时,优先级回到原先优先级。
互斥量是一种特殊的信号量,只有两种状态,开锁和闭锁,他适用于线程多次持有信号量,造成线程递归而死锁以及多个线程的优先级翻转问题。
互斥量初始化时为开锁状态,仅允许一个线程持有,当有线程通过互斥量访问临界区时,互斥量变为闭锁状态,此时其他线程若访问互斥量,则根据设定的时间进行等待,直到互斥量被释放后获取互斥量。
互斥量机制以此保证同一时刻只有一个线程访问临界区。
典型例子:串口通讯时,由于硬件资源只有一个,如果两个线程需要同时发送,则必须加上互斥锁。
ps:互斥量不能在中断服务函数中使用。
高优先级和低优先级使用互斥量访问临界区的过程
互斥量与线程,信号量类似,分为静态和动态,区别为是否由系统分配内存空间,API同样有静态和动态互斥量的创建和删除,互斥量的获取和释放。
rt_mutex_init ( rt_mutex_t mutex, //指向互斥量的指针控制块
const char * name, //互斥量名称
rt_uint8_t flag //互斥量标志位
)
使用时需先创建一个静态互斥量指针(static rt_mutex_t dynamic_mutex = RT_NULL;)之后再进行互斥量的创建就可以了。
rt_mutex_detach (rt_mutex_t mutex) //互斥量的删除
互斥量的删除函数,参数为指向互斥量的指针
rt_mutex_create ( const char * name, //互斥量名称
rt_uint8_t flag //互斥量标志位
)
使用时也需要首先创建一个指向互斥量的指针(static rt_mutex_t dynamic_mutex = RT_NULL;),之后再创建互斥量即可。使用示例如下:
static rt_mutex_t dynamic_mutex = RT_NULL;
/* 创建一个动态互斥量 */
dynamic_mutex = rt_mutex_create("dmutex", RT_IPC_FLAG_FIFO);
rt_mutex_delete ( rt_mutex_t mutex )
互斥量的删除,参数为指向互斥量的指针。
rt_mutex_take ( rt_mutex_t mutex, //互斥量指针
rt_int32_t time //获取互斥量等待时间
)
互斥量的获取无需创建指针,直接调用代码即可。
rt_mutex_release(rt_mutex_t mutex) //互斥量对应指针
互斥量的释放与获取相同,直接调用代码即可。
/*
* 程序清单:互斥锁例程
*
* 互斥锁是一种保护共享资源的方法。当一个线程拥有互斥锁的时候,
* 可以保护共享资源不被其他线程破坏。线程1对2个number分别进行加1操作
* 线程2也会对2个number分别进行加1操作。使用互斥量保证2个number值保持一致
*/
#include
/* 指向互斥量的指针 */
static rt_mutex_t dynamic_mutex = RT_NULL;
static rt_uint8_t number1, number2 = 0;
ALIGN(RT_ALIGN_SIZE)
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);
}
}
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, mumber2 = %d \n", number1, number2);
}
else
{
rt_kprintf("mutex protect ,number1 = mumber2 is %d\n", number1);
}
number1++;
number2++;
rt_mutex_release(dynamic_mutex);
if (number1 >= 50)
return;
}
}
/* 互斥量示例的初始化 */
int main(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;
}
static rt_thread_t thread1=RT_NULL;
thread1=rt_thread_create("thread1",
rt_thread_entry1,
RT_NULL,
256,
5, 5);
rt_thread_startup(thread1);
static rt_thread_t thread2=RT_NULL;
thread2=rt_thread_create("thread2",
rt_thread_entry2,
RT_NULL,
256,
4,
5);
rt_thread_startup(thread2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(main, mutex sample);
参考RT-Thread官方例程,做了一些修改,改为两个动态线程使用互斥量。现象如下:
串口输出,从1到49。