加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

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

(2016-01-03 16:29:49)
标签:

rtx

emwin

cosiii

stm32f407

安富莱

分类: RTX及其中间件

第15章     互斥信号量

本章节开始讲解RTX的另一个重要的资源共享机制---互斥信号量(Mutex,即Mutual Exclusion的缩写)。注意,建议初学者学习完上个章节的信号量后再学习本章节的互斥信号量

本章教程配套的例子含Cortex-M3内核的STM32F103Cortex-M4内核的STM32F407

15.1 互斥信号量

15.2 互斥信号量API函数

15.3 实验例程说明

15.4       总结

 

15.1 互斥信号量

15.1.1  互斥信号量的概念及其作用

互斥信号量就是信号量的一种特殊形式,也就是信号量初始值为1的情况。有些RTOS中也将信号量初始值设置为1的情况称之为二值信号量。为什么叫二值信号量呢?因为信号量资源被获取了,信号量值就是0,信号量资源被释放,信号量值就是1,把这种只有01两种情况的信号量称之为二值信号量。互斥信号量的主要作用就是对资源实现互斥访问。下面举一个通过二值信号量实现资源独享,即互斥访问的例子,让大家有一个形象的认识

运行条件:

u  让两个任务Task1Task2都有运行串口打印printf,这里我们就对函数printf通过二值信号量实现互斥访问。如果不对函数printf进行互斥访问,串口打印容易出现乱码。

u  用信号量实现二值信号量只需将信号量的初始值设置为1即可。

代码实现:

u  创建二值信号量

 

OS_SEM semaphore;

 

 

static void AppObjCreate (void)

{

    

     os_sem_init (semaphore, 0);

}

u  通过二值信号量实现对printf函数互斥访问的两个任务

 

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = 1000;

    

    

     os_itv_set(usFrequency);

    

    while(1)

    {

 

        

os_sem_wait (&semaphore, 0xffff);

         printf("任务AppTaskLED正在运行\r\n");

        

         os_sem_send (&semaphore);

        

        

         os_itv_wait();

    }

}

 

 

__task void AppTaskMsgPro(void)

{

     const uint16_t usFrequency = 1000;

    

    

     os_itv_set(usFrequency);

    

    while(1)

    {

        

os_sem_wait (&semaphore, 0xffff);

         printf("任务AppTaskMsgPro正在运行\r\n");

        

         os_sem_send (&semaphore);

 

        

         os_itv_wait();

    }

}

有了上面二值信号量的认识之后,互斥信号量跟二值信号量又有什么区别呢?互斥信号量可以防止优先级翻转,而二值信号量不支持,下面我们就讲解一下优先级翻转问题。

15.1.2 优先级翻转问题

下面我们通过如下的框图来说明一下优先级翻转的问题,让大家有一个形象的认识。

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

 

运行条件:

u  创建3个任务Task1Task2Task3,优先级分别为321。也就是Task1的优先级最高

u  任务Task1Task3互斥访问串口打印printf,采用二值信号实现互斥访问。

u  起初Task3通过二值信号量正在调用printf,被任务Task1抢占,开始执行任务Task1,也就是上图的起始位置。

运行过程描述如下:

u  任务Task1运行的过程需要调用函数printf,发现任务Task3正在调用,任务Task1会被挂起,等待Task3释放函数printf

u  在调度器的作用下,任务Task3得到运行,Task3运行的过程中,由于任务Task2就绪,抢占了Task3的运行。优先级翻转问题就出在这里了,从任务执行的现象上看,任务Task1需要等待Task2执行完毕才有机会得到执行,这个与抢占式调度正好反了,正常情况下应该是高优先级任务抢占低优先级任务的执行,这里成了高优先级任务Task1等待低优先级任务Task2完成。所以这种情况被称之为优先级翻转问题

u  任务Task2执行完毕后,任务Task3恢复执行,Task3释放互斥资源后,任务Task1得到互斥资源,从而可以继续执行。

上面就是一个产生优先级翻转问题的现象。

15.1.3 RTX互斥信号量的实现

RTX互斥信号量是怎么实现的呢?其实相比二值信号量就是解决了一下优先级翻转的问题。下面我们通过如下的框图来说明一下RTX互斥信号量的实现,让大家有一个形象的认识。

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

 

运行条件:

u  创建2个任务Task1Task2,优先级分别为13,也就是任务Task2的优先级最高

u  任务Task1Task2互斥访问串口打印printf

u  使用RTX的互斥信号量实现串口打印printf的互斥访问。

运行过程描述如下:

u  低优先级任务Task1执行过程中先获得互斥资源printf的执行。此时任务Task2抢占了任务Task1的执行,任务Task1被挂起。任务Task2得到执行。

u  任务Task2执行过程中也需要调用互斥资源,但是发现任务Task1正在访问,此时任务Task1的优先级会被提升到跟Task2同一个优先级,也就是优先级3这个就是所谓的优先级继承(Priority inheritance),这样就有效的防止了优先级翻转问题。任务Task2被挂起,任务Task1有新的优先级继续执行。

u  任务Task1执行完毕并释放互斥资源后,优先级恢复到原来的水平。由于互斥资源可以使用,任务Task2获得互斥资源后开始执行。

 

上面就是一个简单RTX互斥信号量的实现过程。

15.1.4RTX中断方式互斥信号量的实现

互斥信号量仅支持用在RTX的任务中,中断函数中不可使用。

15.2互斥信号量API函数

使用如下3个函数可以实现RTX的互斥信号量:

u  os_mut_init

u  os_mut_release

u  os_mut_wait

关于这3个函数的讲解及其使用方法可以看教程第33.3小节里面说的参考资料rlarm.chm文件

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

 

下面我们也对这3个函数依次进行讲解:

15.2.1 函数os_mut_init

函数原型:

void os_mut_init (

    OS_IDmutex );    

函数描述:

函数os_mut_init用于互斥信号量的初始化并设置初始值。

u  1个参数填写数据类型为OS_MUT的变量,同时也作为ID标识

使用这个函数要注意以下问题:

1.     函数的参数必须是OS_MUT类型的。

使用举例:

#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_IDmutex,       

    U16   timeout );    

函数描述:

函数os_mut_wait用于获取互斥信号量资源,如果互斥资源可用,那么调用函数os_mut_wait后可以成功获取互斥资源,在此函数的源码将计数值加1互斥信号量源码的实现上跟信号量不同)。如果互斥资源不可用,调用此函数的任务将由运行态转到挂起态,等待信号量资源可用,也就是计数值为0的时候。

如果一个低优先级的任务通过互斥信号量正在访问互斥资源,那么当一个高优先级的任务也通过互斥信号量访问这个互斥资源的话,会将这个低优先级任务的优先级提升到和高优先级任务一样的优先级,这就是所谓的优先级继承,通过优先级继承可以有效防止优先级翻转问题。当低优先级任务释放了互斥资源之后,重新恢复到原来的优先级。

u  1个参数填写数据类型为OS_MUT的变量,同时也作为ID标识

u  2个参数表示设在的等待时间,范围0-0xFFFF,当参数设置为0-0xFFFE时,表示等待这么多个时钟节拍,参数设置为0xFFFF时表示无限等待直到互斥资源可用。

u  函数返回OS_R_MUT表示函数设置的超时时间范围内收到互斥信号量可用资源。

函数返回OS_R_TMO表示超时。

函数返回OS_R_OK表示无需等待,立即获得互斥资源。

使用这个函数要注意以下问题:

1.     使用此函数前一定要调用函数os_mut_init进行初始化。

使用举例:

#include

 

static OS_MUT mutex;

 

 

__task void AppTaskLED(void)

{

const uint16_t usFrequency = 1000;

         

 

os_itv_set(usFrequency);

         

    while(1)

    {

         bsp_LedToggle(2);

         bsp_LedToggle(3);

 

        

         os_mut_wait (&mutex, 0xffff);

         printf("任务AppTaskLED正在运行\r\n");

        

         os_mut_release (&mutex);

                   

        

         os_itv_wait();

    }

}

15.2.3函数os_mut_release

函数原型:

OS_RESULT os_mut_release (

        OS_IDmutex );   

函数描述:

函数os_mut_release用于释放互斥资源,调用此函数会将计数值减1。只有当计数值减到0的时候其它的任务才可以获取互斥资源。也就是说如果用户调用os_mut_waitos_mut_release,需要配套使用。通过函数os_mut_wait实现互斥信号量计数值加1,通过函数os_mut_release实现互斥信号量计数值减1操作,这样的话,这两个函数可以实现嵌套调用,但是一定要保证成对调用,要不会造成互斥资源无法正确释放。

如果拥有互斥资源的任务的优先级被提升了,那么此函数会恢复任务以前的优先级。

u  1个参数参数填写数据类型为OS_MUT的变量,同时也作为ID标识。

u  返回值OS_R_OK,表示互斥信号量成功释放。

返回值OS_R_NOR,表示互斥信号量的内部计数值已经是0或者调用此函数的任务不是互斥资源的拥有者。

使用这个函数要注意以下问题:

1.     使用此函数前一定要调用函数os_mut_init进行初始化。

使用举例:

#include

 

static OS_MUT mutex;

 

 

__task void AppTaskLED(void)

{

const uint16_t usFrequency = 1000;

         

 

os_itv_set(usFrequency);

         

    while(1)

    {

         bsp_LedToggle(2);

         bsp_LedToggle(3);

 

        

         os_mut_wait (&mutex, 0xffff);

         printf("任务AppTaskLED正在运行\r\n");

        

         os_mut_release (&mutex);

                   

        

         os_itv_wait();

    }

}

 

15.3实验例程说明

15.3.1STM32F103开发板实验

配套例子:

V4-413_RTX实验_互斥信号量

实验目的:

1. 学习RTX的互斥信号量

实验内容:

1. K1按键按下,串口打印。

2. 在调用printf函数的地方都加上互斥信号量,防止多个任务调用此函数造成冲突,以至于串口打印出现乱码。

3. 各个任务实现的功能如下:

AppTaskUserIF任务  :按键消息处理。

AppTaskLED任务     LED闪烁和串口打印。

AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。

AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。

RTX配置:

RTX配置向导详情如下:

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

 

u  Task Configuration

l  Number of concurrent running tasks

允许创建4个任务,实际创建了如下四个任务

              AppTaskUserIF任务   :按键消息处理。

              AppTaskLED任务     LED闪烁和串口打印

              AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。

              AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。

l  Number of tasks with user-provided stack

创建的4个任务都是采用自定义堆栈方式。

RTX任务调试信息:

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

 

程序设计:

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  系统栈大小分配:

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

 

u  RTX初始化:

 

int main (void)

  

    

     bsp_Init();

    

    

     os_sys_init_user (AppTaskStart,            

                       4,                       

                       &AppTaskStartStk,        

                       sizeof(AppTaskStartStk));

     while(1);

}

u  RTX任务创建:

 

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,            

                                           1,                        

                                           &AppTaskUserIFStk,        

                                           sizeof(AppTaskUserIFStk));

    

     HandleTaskLED = os_tsk_create_user(AppTaskLED,             

                                        2,                      

                                        &AppTaskLEDStk,         

                                        sizeof(AppTaskLEDStk)); 

    

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,            

                                           3,                        

                                           &AppTaskMsgProStk,        

                                           sizeof(AppTaskMsgProStk));

}

u  互斥信号量创建

static OS_MUT mutex;

 

 

u  四个RTX任务的实现:

 

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

 

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                  

                   case KEY_DOWN_K1:

                      

                       os_mut_wait (&mutex, 0xffff);

                       printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");

                      

                       os_mut_release (&mutex);

                       break;  

 

                  

                   default:                    

                       break;

              }

         }

        

         os_dly_wait(20);

     }

}

 

 

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = 1000;

    

    

     os_itv_set(usFrequency);

    

    while(1)

    {

         bsp_LedToggle(2);

         bsp_LedToggle(3);

 

        

         os_mut_wait (&mutex, 0xffff);

         printf("任务AppTaskLED正在运行\r\n");

        

         os_mut_release (&mutex);

        

        

         os_itv_wait();

    }

}

 

 

__task void AppTaskMsgPro(void)

{

     const uint16_t usFrequency = 1000;

    

    

     os_itv_set(usFrequency);

    

    while(1)

    {

         bsp_LedToggle(1);

         bsp_LedToggle(4);

 

        

         os_mut_wait (&mutex, 0xffff);

         printf("任务AppTaskMsgPro正在运行\r\n");

        

         os_mut_release (&mutex);

 

        

         os_itv_wait();

    }

 

}

 

 

__task void AppTaskStart(void)

{

    

     AppTaskCreate();

    

    

     AppObjCreate();

    

    while(1)

    {

        

         bsp_KeyScan();

        os_dly_wait(10);

    }

}

15.3.2STM32F407开发板实验

配套例子:

V4-413_RTX实验_互斥信号量

实验目的:

1. 学习RTX的互斥信号量

实验内容:

1. K1按键按下,串口打印。

2. 在调用printf函数的地方都加上互斥信号量,防止多个任务调用此函数造成冲突,以至于串口打印出现乱码。

3. 各个任务实现的功能如下:

AppTaskUserIF任务 :按键消息处理。

AppTaskLED任务     LED闪烁和串口打印。

AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。

AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。

RTX配置:

RTX配置向导详情如下:

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

 

u  Task Configuration

l  Number of concurrent running tasks

允许创建4个任务,实际创建了如下四个任务

              AppTaskUserIF任务   :按键消息处理。

  AppTaskLED任务     LED闪烁和串口打印。

AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。

              AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。

l  Number of tasks with user-provided stack

创建的4个任务都是采用自定义堆栈方式。

RTX任务调试信息:

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

 

程序设计:

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  系统栈大小分配:

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

 

u  RTX初始化:

 

int main (void)

  

    

     bsp_Init();

    

    

     os_sys_init_user (AppTaskStart,            

                       4,                       

                       &AppTaskStartStk,        

                       sizeof(AppTaskStartStk));

     while(1);

}

u  RTX任务创建:

 

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,            

                                           1,                        

                                           &AppTaskUserIFStk,        

                                           sizeof(AppTaskUserIFStk));

    

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              

                                        2,                      

                                        &AppTaskLEDStk,         

                                        sizeof(AppTaskLEDStk)); 

    

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,            

                                           3,                        

                                           &AppTaskMsgProStk,        

                                           sizeof(AppTaskMsgProStk));

}

u  互斥信号量创建

static OS_MUT mutex;

 

 

u  四个RTX任务的实现:

 

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

 

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                  

                   case KEY_DOWN_K1:

                      

                       os_mut_wait (&mutex, 0xffff);

                       printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");

                      

                       os_mut_release (&mutex);

                       break;  

 

                  

                   default:                    

                       break;

              }

         }

        

         os_dly_wait(20);

     }

}

 

 

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = 1000;

    

    

     os_itv_set(usFrequency);

    

    while(1)

    {

         bsp_LedToggle(2);

         bsp_LedToggle(3);

 

        

         os_mut_wait (&mutex, 0xffff);

         printf("任务AppTaskLED正在运行\r\n");

        

         os_mut_release (&mutex);

        

        

         os_itv_wait();

    }

}

 

 

__task void AppTaskMsgPro(void)

{

     const uint16_t usFrequency = 1000;

    

    

     os_itv_set(usFrequency);

    

    while(1)

    {

         bsp_LedToggle(1);

         bsp_LedToggle(4);

 

        

         os_mut_wait (&mutex, 0xffff);

         printf("任务AppTaskMsgPro正在运行\r\n");

        

         os_mut_release (&mutex);

 

        

         os_itv_wait();

    }

 

}

 

 

__task void AppTaskStart(void)

{

    

     AppTaskCreate();

    

    

     AppObjCreate();

    

    while(1)

    {

        

         bsp_KeyScan();

        os_dly_wait(10);

    }

}

15.4总结

本章节主要为大家讲解了另一个重要的资源共享机制-互斥信号量,其中优先级翻转是互斥信号量中一个比较重要的概念,初学者要花些时间去掌握这个知识点。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有