【RTX操作系统教程】第15章 互斥信号量

标签:
rtxemwincosiiistm32f407安富莱 |
分类: RTX及其中间件 |
第15章
互斥信号量
本章节开始讲解RTX的另一个重要的资源共享机制---互斥信号量(Mutex,即Mutual Exclusion的缩写)。注意,建议初学者学习完上个章节的信号量后再学习本章节的互斥信号量。
本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407。
15.1 互斥信号量
15.2 互斥信号量API函数
15.3 实验例程说明
15.4
15.1 互斥信号量
15.1.1 互斥信号量的概念及其作用
互斥信号量就是信号量的一种特殊形式,也就是信号量初始值为1的情况。有些RTOS中也将信号量初始值设置为1的情况称之为二值信号量。为什么叫二值信号量呢?因为信号量资源被获取了,信号量值就是0,信号量资源被释放,信号量值就是1,把这种只有0和1两种情况的信号量称之为二值信号量。互斥信号量的主要作用就是对资源实现互斥访问。下面举一个通过二值信号量实现资源独享,即互斥访问的例子,让大家有一个形象的认识
运行条件:
u
u
代码实现:
u
OS_SEM semaphore;
static void AppObjCreate (void)
{
}
u
__task void AppTaskLED(void)
{
os_sem_wait (&semaphore, 0xffff);
}
__task void AppTaskMsgPro(void)
{
os_sem_wait (&semaphore, 0xffff);
}
有了上面二值信号量的认识之后,互斥信号量跟二值信号量又有什么区别呢?互斥信号量可以防止优先级翻转,而二值信号量不支持,下面我们就讲解一下优先级翻转问题。
15.1.2 优先级翻转问题
下面我们通过如下的框图来说明一下优先级翻转的问题,让大家有一个形象的认识。
运行条件:
u
u
u
运行过程描述如下:
u
u
u
上面就是一个产生优先级翻转问题的现象。
15.1.3 RTX 互斥信号量的实现
RTX互斥信号量是怎么实现的呢?其实相比二值信号量就是解决了一下优先级翻转的问题。下面我们通过如下的框图来说明一下RTX互斥信号量的实现,让大家有一个形象的认识。
运行条件:
u
u
u
运行过程描述如下:
u
u
u
上面就是一个简单RTX互斥信号量的实现过程。
15.1.4RTX中断方式互斥信号量的实现
互斥信号量仅支持用在RTX的任务中,中断函数中不可使用。
15.2互斥信号量API函数
使用如下3个函数可以实现RTX的互斥信号量:
u
u
u
关于这3个函数的讲解及其使用方法可以看教程第3章3.3小节里面说的参考资料rlarm.chm文件
下面我们也对这3个函数依次进行讲解:
15.2.1 函数os_mut_init
函数原型:
void os_mut_init (
函数描述:
函数os_mut_init用于互斥信号量的初始化并设置初始值。
u
使用这个函数要注意以下问题:
1.
使用举例:
#include
static OS_MUT mutex;
static void AppObjCreate (void)
{
os_mut_init (&mutex);
}
15.2.2函数os_mut_wait
函数原型:
OS_RESULT os_mut_wait (
函数描述:
函数os_mut_wait用于获取互斥信号量资源,如果互斥资源可用,那么调用函数os_mut_wait后可以成功获取互斥资源,在此函数的源码将计数值加1(互斥信号量源码的实现上跟信号量不同)。如果互斥资源不可用,调用此函数的任务将由运行态转到挂起态,等待信号量资源可用,也就是计数值为0的时候。
如果一个低优先级的任务通过互斥信号量正在访问互斥资源,那么当一个高优先级的任务也通过互斥信号量访问这个互斥资源的话,会将这个低优先级任务的优先级提升到和高优先级任务一样的优先级,这就是所谓的优先级继承,通过优先级继承可以有效防止优先级翻转问题。当低优先级任务释放了互斥资源之后,重新恢复到原来的优先级。
u
u
u
函数返回OS_R_TMO表示超时。
函数返回OS_R_OK表示无需等待,立即获得互斥资源。
使用这个函数要注意以下问题:
1.
使用举例:
#include
static OS_MUT mutex;
__task void AppTaskLED(void)
{
const uint16_t usFrequency = 1000;
os_itv_set(usFrequency);
}
15.2.3函数os_mut_release
函数原型:
OS_RESULT os_mut_release (
函数描述:
函数os_mut_release用于释放互斥资源,调用此函数会将计数值减1。只有当计数值减到0的时候其它的任务才可以获取互斥资源。也就是说如果用户调用os_mut_wait和os_mut_release,需要配套使用。通过函数os_mut_wait实现互斥信号量计数值加1,通过函数os_mut_release实现互斥信号量计数值减1操作,这样的话,这两个函数可以实现嵌套调用,但是一定要保证成对调用,要不会造成互斥资源无法正确释放。
如果拥有互斥资源的任务的优先级被提升了,那么此函数会恢复任务以前的优先级。
u
u
返回值OS_R_NOR,表示互斥信号量的内部计数值已经是0或者调用此函数的任务不是互斥资源的拥有者。
使用这个函数要注意以下问题:
1.
使用举例:
#include
static OS_MUT mutex;
__task void AppTaskLED(void)
{
const uint16_t usFrequency = 1000;
os_itv_set(usFrequency);
}
15.3实验例程说明
15.3.1STM32F103开发板实验
配套例子:
V4-413_RTX实验_互斥信号量
实验目的:
1. 学习RTX的互斥信号量
实验内容:
1. K1按键按下,串口打印。
2. 在调用printf函数的地方都加上互斥信号量,防止多个任务调用此函数造成冲突,以至于串口打印出现乱码。
3. 各个任务实现的功能如下:
AppTaskUserIF任务
AppTaskLED任务
AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。
AppTaskStart任务
RTX配置:
RTX配置向导详情如下:
u
l
允许创建4个任务,实际创建了如下四个任务
l
创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:
程序设计:
u
static uint64_t
AppTaskUserIFStk[512/8];
static uint64_t
AppTaskLEDStk[256/8];
static uint64_t
AppTaskMsgProStk[512/8];
static uint64_t
AppTaskStartStk[512/8];
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
u
u
int main (void)
{
}
u
static void AppTaskCreate (void)
{
}
u
static OS_MUT mutex;
u
__task void AppTaskUserIF(void)
{
}
__task void AppTaskLED(void)
{
}
__task void AppTaskMsgPro(void)
{
}
__task void AppTaskStart(void)
{
}
15.3.2STM32F407开发板实验
配套例子:
V4-413_RTX实验_互斥信号量
实验目的:
1. 学习RTX的互斥信号量
实验内容:
1. K1按键按下,串口打印。
2. 在调用printf函数的地方都加上互斥信号量,防止多个任务调用此函数造成冲突,以至于串口打印出现乱码。
3. 各个任务实现的功能如下:
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务
AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。
AppTaskStart任务
RTX配置:
RTX配置向导详情如下:
u
l
允许创建4个任务,实际创建了如下四个任务
AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。
l
创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:
程序设计:
u
static uint64_t
AppTaskUserIFStk[512/8];
static uint64_t
AppTaskLEDStk[256/8];
static uint64_t
AppTaskMsgProStk[512/8];
static uint64_t
AppTaskStartStk[512/8];
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
u
u
int main (void)
{
}
u
static void AppTaskCreate (void)
{
}
u
static OS_MUT mutex;
u
__task void AppTaskUserIF(void)
{
}
__task void AppTaskLED(void)
{
}
__task void AppTaskMsgPro(void)
{
}
__task void AppTaskStart(void)
{
}
15.4总结
本章节主要为大家讲解了另一个重要的资源共享机制-互斥信号量,其中优先级翻转是互斥信号量中一个比较重要的概念,初学者要花些时间去掌握这个知识点。