精华内容
下载资源
问答
  • 此程序为STM32F103 移植FreeRTOS系统工程源码,有需要可以学习参考,特别适合刚入门, 或者还没有入门的单片机开发者,可以提高代码开发周期,提升编程水平
  • 6个任务 1、任务1控制闪烁3个指示灯,为周期性任务 2、任务2控制闪烁2个指示灯,为周期性任务 ... 要想使用互斥信号量,要在 FreeRTOSConfig.h 文件中使能宏定义: #define configUSE_MUTEXES 1
  • ucosii 互斥信号量

    2016-07-17 11:36:58
    ucosii 互斥信号量
  • 互斥信号量

    千次阅读 2021-05-08 16:40:32
    为使多个进程能互斥的访问某临界资源,只需为该资源设置一互斥信号量mutex,并设初始值为1,然后将各进程访问该资源的临界区置于wait(mutex)和signal(mutex)操作之间即可。 互斥信号量值的描述: 初值为1,取值范围...

    临界资源必须是互斥的访问,即一段时间内只能让一个进程访问,对应临界区。
    p(s)—申请资源—wait(s)
    v(s)—释放资源—signal(s)

    为使多个进程能互斥的访问某临界资源,只需为该资源设置一互斥信号量mutex,并设初始值为1,然后将各进程访问该资源的临界区置于wait(mutex)和signal(mutex)操作之间即可。

    互斥信号量值的描述:
    初值为1,取值范围为(-1,0,1)。
    当 mutex=1 时,表示两个进程皆未进入需要互斥的临界区;
    当 mutex=0 时,表示有一个进程进入临界区运行,另外一个必须等待,改入阻塞队列;
    当 mutex=-1时,表示有一个进程正在临界区运行,另一个进程因等待而阻塞在信号量队列中,需要被当前已在临界区运行的进程退出时唤醒。

    展开全文
  • ucos 互斥信号量

    2018-08-16 11:58:11
    ucosIII示例-互斥信号量,初始化和使用方法,可直接运行
  • 本章节讲解 FreeRTOS 任务间的同步和资源共享机制...FreeRTOS 分别提供了二值信号量和计数信号量,其中二值信号量可以理解成计数信号量的一种特殊形式,即初始化为仅有一个资源可以使用,只不过 FreeRTOS 对这两种都...

    以下转载自安富莱电子: http://forum.armfly.com/forum.php

    本章节讲解 FreeRTOS 任务间的同步和资源共享机制,二值信号量。 二值信号量是计数信号量的一种特殊形式,即共享资源为 1 的情况。

    FreeRTOS 分别提供了二值信号量和计数信号量,其中二值信号量可以理解成计数
    信号量的一种特殊形式,即初始化为仅有一个资源可以使用,只不过 FreeRTOS 对这两种都提供了 API
    函数,而像 RTX,uCOS-II 和 III 是仅提供了一个信号量功能,设置不同的初始值就可以分别实现二值信
    号量和计数信号量。 当然,FreeRTOS 使用计数信号量也能够实现同样的效果。 另外,为什么叫二值信号
    量呢?因为信号量资源被获取了,信号量值就是 0,信号量资源被释放,信号量值就是 1,把这种只有 0
    和 1 两种情况的信号量称之为二值信号量

    函数 xSemaphoreCreateBinary
    函数原型:
    SemaphoreHandle_t xSemaphoreCreateBinary(void)
    函数描述:
    函数 xSemaphoreCreateBinary 用于创建二值信号量。
    返回值,如果创建成功会返回二值信号量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不
    足,无法为此二值信号量提供所需的空间会返回 NULL。

    FreeRTOS 重要的资源共享机制---互斥信号量(Mutex,即 Mutual Exclusion 的缩写)。
    注意,建议初学者学习完前两个信号量后再学习本章节的互斥信号量

    互斥信号量的主要作用是对资源实现互斥访问,使用二值信号量也可以实现互斥访问的功能,不过互
    斥信号量与二值信号量有区别。 下面我们先举一个通过二值信号量实现资源独享,即互斥访问的例子,让
    大家有一个形象的认识,进而引出要讲解的互斥信号量。
    运行条件:
    让两个任务 Task1 和 Task2 都运行串口打印函数 printf,这里我们就通过二值信号量实现对函数
    printf 的互斥访问。 如果不对函数 printf 进行互斥访问,串口打印容易出现乱码。
    用计数信号量实现二值信号量只需将计数信号量的初始值设置为 1 即可。
    代码实现:

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

     

    运行条件:
    创建 3 个任务 Task1,Task2 和 Task3,优先级分别为 3,2,1。 也就是 Task1 的优先级最高。
    任务 Task1 和 Task3 互斥访问串口打印 printf,采用二值信号实现互斥访问。
    起初 Task3 通过二值信号量正在调用 printf,被任务 Task1 抢占,开始执行任务 Task1,也就是上图的起始位置。 
    运行过程描述如下:
    任务 Task1 运行的过程需要调用函数 printf,发现任务 Task3 正在调用,任务 Task1 会被挂起,等
    待 Task3 释放函数 printf。
    在调度器的作用下,任务 Task3 得到运行,Task3 运行的过程中,由于任务 Task2 就绪,抢占了 Task3
    的运行。 优先级翻转问题就出在这里了,从任务执行的现象上看,任务 Task1 需要等待 Task2 执行
    完毕才有机会得到执行,这个与抢占式调度正好反了,正常情况下应该是高优先级任务抢占低优先级
    任务的执行,这里成了高优先级任务 Task1 等待低优先级任务 Task2 完成。 所以这种情况被称之为
    优先级翻转问题
    任务 Task2 执行完毕后,任务 Task3 恢复执行,Task3 释放互斥资源后,任务 Task1 得到互斥资源,
    从而可以继续执行。
    FreeRTOS 互斥信号量的实现
    FreeRTOS 互斥信号量是怎么实现的呢?其实相对于二值信号量,互斥信号量就是解决了一下优先级
    翻转的问题。 下面我们通过如下的框图来说明一下 FreeRTOS 互斥信号量的实现,让大家有一个形象的认识。

    运行条件:
    创建 2 个任务 Task1 和 Task2,优先级分别为 1 和 3,也就是任务 Task2 的优先级最高。
    任务 Task1 和 Task2 互斥访问串口打印 printf。
    使用 FreeRTOS 的互斥信号量实现串口打印 printf 的互斥访问。
    运行过程描述如下:
    低优先级任务 Task1 执行过程中先获得互斥资源 printf 的执行。 此时任务 Task2 抢占了任务 Task1
    的执行,任务 Task1 被挂起。 任务 Task2 得到执行。
    任务 Task2 执行过程中也需要调用互斥资源,但是发现任务 Task1 正在访问,此时任务 Task1 的优
    先级会被提升到与 Task2 同一个优先级,也就是优先级 3,这个就是所谓的优先级继承(Priority
    inheritance),这样就有效地防止了优先级翻转问题。 任务 Task2 被挂起,任务 Task1 有新的优先
    级继续执行。
    任务 Task1 执行完毕并释放互斥资源后,优先级恢复到原来的水平。 由于互斥资源可以使用,任务
    Task2 获得互斥资源后开始执行。
    FreeRTOS 中断方式互斥信号量的实现
    互斥信号量仅支持用在 FreeRTOS 的任务中,中断函数中不可使用。
    互斥信号量 API 函数
    函数 xSemaphoreCreateMutex
    函数原型:
    SemaphoreHandle_t xSemaphoreCreateMutex( void )
    函数描述:
    函数 xSemaphoreCreateMutex 用于创建互斥信号量。
    返回值,如果创建成功会返回互斥信号量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不
    足,无法为此互斥信号量提供所需的空间会返回 NULL。
    使用这个函数要注意以下问题:
    1. 此函数是基于函数 xQueueCreateMutex 实现的:
    #define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
    函数 xQueueCreateMutex 的实现是基于消息队列函数 xQueueGenericCreate 实现的。
    2. 使用此函数要在 FreeRTOSConfig.h 文件中使能宏定义:
    #define configUSE_MUTEXES 1

    互斥信号量, xSemaphoreTake 和 xSemaphoreGive 一定要成对的调用 

    应用举例:

     


     经过测试,互斥信号量是可以被其他任务释放的,但是我们最好不要这么做,因为官方推荐的就是在同一个任务中接收和释放。如果在其他任务释放,不仅仅会让代码整体逻辑变得复杂,还会给使用和维护这套API的人带来困难。遵守规范,总是好的。

    裸机编程的时候,我经常想一个问题,就是怎么做到当一个标志位触发的时候,立即执行某个操作,如同实现标志中断一样,在os编程之后,我们就可以让一个优先级最高任务一直等待某个信号量,如果获得信号量,就执行某个操作,实现类似标志位中断的作用(当然,要想正真做到中断效果,那就需要屏蔽所有可屏蔽中断,而临界区就可以做到)。

    再说一下递归互斥信号量:递归互斥信号量,其实就是互斥信号量里面嵌套互斥信号量

    eg:

    static void vTaskMsgPro(void *pvParameters)
    {
        TickType_t xLastWakeTime;
        const TickType_t xFrequency = 1500;
    
        /* 获取当前的系统时间 */
        xLastWakeTime = xTaskGetTickCount();
        
        while(1)
        {
            /* 递归互斥信号量,其实就是互斥信号量里面嵌套互斥信号量 */
            xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);
            {
                /* -------------------------------------- */
                   //假如这里是被保护的资源,第1层被保护的资源,用户可以在这里添加被保护资源
                /* ---------------------------------------------------------------------------- */
                printf("任务vTaskMsgPro在运行,第1层被保护的资源,用户可以在这里添加被保护资源\r\n");
                
                /* 第1层被保护的资源里面嵌套被保护的资源 */
                xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);
                {
                    /* ------------------------------------------------------------------------ */
                    //假如这里是被保护的资源,第2层被保护的资源,用户可以在这里添加被保护资源
                    /* ------------------------------------------------------------------------ */
                    printf("任务vTaskMsgPro在运行,第2层被保护的资源,用户可以在这里添加被保护资源\r\n");
                    
                    /* 第2层被保护的资源里面嵌套被保护的资源 */
                    xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);
                    {
                        printf("任务vTaskMsgPro在运行,第3层被保护的资源,用户可以在这里添加被保护资源\r\n");
                        bsp_LedToggle(1);
                        bsp_LedToggle(4);
                    }
                    xSemaphoreGiveRecursive(xRecursiveMutex);
                }
                xSemaphoreGiveRecursive(xRecursiveMutex);    
            }
            xSemaphoreGiveRecursive(xRecursiveMutex);
            
            /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
            vTaskDelayUntil(&xLastWakeTime, xFrequency);
        }
    }

     

    可以前面的那个官方文档那样用if判断传递共享量:

    也可以用我们递归互斥信号量里面的portMAX_DELAY指定永久等待,即xSemaphoreTakeRecursive返回的不是pdTRUE时,会一直等待信号量,直到有信号量来才执行后面的语句。

     

    展开全文
  • 互斥信号量与信号量的理解

    万次阅读 多人点赞 2018-09-17 10:10:24
     至于信号量,和互斥信号量是用区别的,简单来说(个人理解,欢迎纠正)就是互斥信号量再同一时刻,任务得到互斥信号量量后是独占共享资源的,在他没有释放信号量之前,任何其他任务都是不能访问共享资源的。...

    ucos看了也有一周多了,索性源码都能开得懂,并且能去理解。昨天一开始看事件标志组的时候确实不知道怎么回事,后来百度一下,明白了事件标志组的作用以后,再去看书上的讲解和原码就清晰多了,很容易就明白了他的基本运行机理。这也给了我一点启示,学一个东西,看一个东西之前,你最少要知道他干嘛用的,如果连干嘛用的都知道书看的再熟也是枉然。
        ucos中提供了好几个用于同步事件以及共享资源访问的机制,目前我看明白的有信号量,互斥信号量,事件标志组。下面谈谈自己对他们的理解:

    1.互斥信号量:
    互斥互斥,意思就是我用了你就不能用,你用了我就不能用。永远都只有一个人独占这个东西~!举个例子:比如说打印机。
    我任务1现在让他打印《静夜思》,那么在我还没打印完之前,别的任务就不能命令打印机去打印别的东西。否则如果任务2让他打印《春晓》,那最后打印出来的会是什么~????反正肯定不是任务1想要的,肯定也不是任务2想要的。
    上面讲的比较通俗。打印机就是共享资源,谁都可以访问他~!但是同一时间,肯定要保证只有1个任务再操作打印机。那样才能得到大家想要的结果。也就是要独占共享资源的访问权~!

    ucos2中通过互斥信号量来解决这个问题。简单说就是任务1开始访问打印机的时候,先去查询这个互斥信号量是否有效,有效,说明没人在访问打印机,这时任务1就把这个互斥信号量置无效,然后开始操作打印机。这样,每个任务再操作打印机前都要去查询这个互斥信号量时候有效。无效就等,等到有效才可以访问,或者等到不耐烦了(术语叫等待超时)就不等了~!任务一直到用完了打印机后才把信号量置有效,这时其他任务才有可能去访问,操作打印机。

    这里又有一个问题:再任务1操作打印机器件,可能有多个任务申请打印机的所有权。那么再任务1结束后,我应该给谁用呢~~??也许我们马上就反应过来了~废话~!!当然是排队了~~谁先到的谁用啊~~~。没错,这是一种机制,谁最先等待共享资源,就给谁用。但是~!再ucos里面2.52版本还不支持这种方式。他用的另外一种方法!如果你和你BOSS都再等着用打印机,你先到的,这个时候任务1结束了对打印机的操作。你说你敢先用么~???(除非你第二天不想干了~~)你肯定先让老板先用,这就是ucos的实现方式,基于优先级,任务1结束对打印机的操作后,ucos再等待队列中看那个等待任务优先级最高,就先给他用~!即使他是最晚才等待的~!!(这就是BOSS的威力~!)
    关于事件等待列表,有兴趣的可以去看看事件控制块ECB的内容,不在本文讨论。当然,ucos中的互斥信号量还有许多要素,比如说他的继承优先级之类的。本文旨在说明它是干嘛用的,至于其他请参考相关书籍。
    下面的图解释了互斥信号量的基本用法:(简单的两个任务,没有包含多任务等待的情况)
     
    2.信号量:
        至于信号量,和互斥信号量是用区别的,简单来说(个人理解,欢迎纠正)就是互斥信号量再同一时刻,任务得到互斥信号量量后是独占共享资源的,在他没有释放信号量之前,任何其他任务都是不能访问共享资源的。而信号量的不同在于。信号量可以设定一个值,允许最多又几个任务同时去访问共享资源。比如我给他设定一个5,那么对多就有5个任务能同时访问共享资源。每个任务获得信号量的时候就把信号量计数器减去1,这样,再第五个任务获取后,计数器是0.当第六个任务要去访问的时候申请信号量就只能等待了,等到之前的任务发一个信号出来,这样第六个任务才能去访问共享资源。

    互斥信号量可以看成特殊情况下的信号量,他的计数器就是0或者1,只在这两个之间徘徊。
    举个例子(不一定恰当,欢迎纠正):
    现在有很多串口扩展卡,一张卡能扩展出好几个串口,比如说4个,这个扩展卡就是一个共享资源。现在定义一个信号量semcom,初始给他4,那么可以有4个任务去访问这个资源,他每次就给这4个任务分配不同的串口。每个任务要访问这个扩展卡就要去测试semcom看看他时候有信号。这样,前4个任务申请信号后,信号量计数器就等于0了,这样,在第五个任务要去访问扩展卡的时候,他也去测试这个semcom,发现信号量无效,他只能等了~!等到之前的任务释放一个串口为止,如果不用信号量,那么任务五可能就会去访问扩展卡上的串口1,而串口1之前已经分配给了任务1了,~造成什么后果就自己想想吧~~~~如果用互斥信号量,那么无疑浪费了资源,~~~那你就买个扩展1个串口的卡就行了~~你买个扩展4个的然后你用互斥信号量~~~不是摆明再说你是富二代么~~~
    等待信号的任务在有信号以后也是按照等待列表中优先级最高的任务先得到信号处理。有关信号量的具体数据结构参考事件控制块ECB的内容,具体操作参考信号量函数等。在此不做介绍
    下面这个图说明了以上的例子:(有图有真相~!)



    3.事件标志组:
        在理解信号量和互斥信号量的时候都可以类比,因为他们在ucos2里面都通过相同的时间控制块即ECB这个数据结构来实现,理解了一个就很好能看懂另外一个,设置更后面的邮箱和消息队列,也能和信号量之类的类比来学习,他们都有通过ECB来维护。但是事件标志组比较特别,他是ucos2所有这些内核事件里面没有用到ECB的。他有自己的做法。不太合群。什么是事件标志组?
        上面说的信号量,互斥信号量。都是用来同步任务对共享资源的访问,防止冲突而设立的。事件标志组----他是用来同步几个任务,协调几个任务工作而设立的。打个比方你现在要打个电话,打电话这个任务要执行,你必须有手机吧!那你要先执行买手机这个任务,你手机有了,没话费~你也大不了吧~,也就是说打电话这个任务要等买手机这个任务和充话费这个任务都完成了以后你才能去开始打电话这个任务。事件标志组就是用来标志买手机或者充话费这两个任务完成了没有。完成了的话他们会相应的置位事件标志组里面的某些标志位。那么打电话这个任务。发现事件标志组里面买手机对应的位和充话费对应的位都置位了以后就明白,现在可以开始打电话了~!实际中比如你想要读数据,那你肯定要等数据采集更新好了以后你去读才有意义吧~所以数据采集和读取数据这两个任务也可以用事件标志组来实现。当然,事件标志组不一定只用于两个任务之间,通过对头文件的修改,可以让事件标志组达到32位,你可以用事件标志组来协调多个任务的合理运行。达到你预期想达到的目的!事件标志组就是专门干这个活的。
        事件标志组的结构比其他的会复杂一点。没一个事件标志组都维护这自己的一个等待队列的双向链表。每个事件标志组的节点里面都有一个指针和相应的任务控制块TCB一一对应。至于事件标志组的具体实现方法,可以自己去看看源代码。只要
    懂得一些浅显的双向链表的知识,大概理解他的运作机制不会很难。
       

     

    互斥信号量:只有一个线程独占资源,其他子任务在访问资源时需要判断信号是否满足,不满足则等待;

    信号量:同时满足多个任务执行,当超过这个信号量个数时,其他任务需等待。

     

    展开全文
  • 嵌入式系统FreeRTOS — 互斥信号量

    千次阅读 2019-08-14 13:47:32
    互斥信号量可以在资源保护的时候很有帮助。用于控制在两个或多个任务间访问共享资源。任务1里面用互斥,那么任务2只能等任务1访问完再访问同一个变量。 比如全局变量double gADC_value[CH_ADC_NUM] = {0}; //7 adc ...

    互斥信号量可以在资源保护的时候很有帮助。用于控制在两个或多个任务间访问共享资源。任务1里面用互斥,那么任务2只能等任务1访问完再访问同一个变量。

    比如全局变量double gADC_value[CH_ADC_NUM] = {0};      //7 adc channals

    任务1中:周期100ms的采样任务,并保存数据都全局数组中。

               osMutexWait(myMutex01Handle, osWaitForever);
                gADC_value[adc_index] = Get_ADS1259_ADC_Value();
                osMutexRelease(myMutex01Handle);

    任务2中:  周期1000ms的采样任务,并读取保存在全局数组中的adc数据。

            osMutexWait(myMutex01Handle, osWaitForever);
            TC355_ADC_Value = gADC_value[CH_TMCOM2];
            TC314_ADC_Value = gADC_value[CH_TMCOM1];
            osMutexRelease(myMutex01Handle);

    以下参考:

    https://mp.weixin.qq.com/s?src=11&timestamp=1565745402&ver=1789&signature=qe1QZ6HPJRk6tu9AxDhfQCwtlsxg-7pc3ce9vjSpWHkNrvz-epufym9ZTh1qxLs71Y9Kl5Jj4xvcAr*JWBTv0oHXYA7-t1Or9kDhsiJm2VL6TEBoyGBXx8qH7UL8vJ-V&new=1

    1 、互 斥 信 号 量

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

    互斥信号量的主要作用是对资源实现互斥访问,使用二值信号量也可以实现互斥访问的功能,不过互斥信号量与二值信号量有区别。下面我们先举一个通过二值信号量实现资源独享,即互斥访问的例子,让大家有一个形象的认识,进而引出要讲解的互斥信号量。

    运行条件:  

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

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

    代码实现:

    创建二值信号量

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

    static SemaphoreHandle_t xSemaphore = NULL;
    // * 函 数 名: AppObjCreate
    // * 功能说明: 创建任务通信机制
    // * 形 参: 无
    // * 返 回 值: 无
    static void AppObjCreate (void)
    {
        /* 创建二值信号量,首次创建信号量计数值是 0 */
        xSemaphore = xSemaphoreCreateBinary();
        if (xSemaphore == NULL)
        {
            /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
        }/* 先释放一次,将初始值改为 1,利用二值信号量实现互斥功能 */
        //xSemaphoreGive(xSemaphore);这句可以不用
    }
    //通过二值信号量实现对 printf 函数互斥访问的两个任务
    
    static void vTaskLED(void *pvParameters)
    {
        TickType_t xLastWakeTime;
        const TickType_t xFrequency = 300;/* 获取当前的系统时间 */
        xLastWakeTime = xTaskGetTickCount();
        while (1)
        {
            /* 通过二值信号量实现资源互斥访问,永久等待直到资源可用 */
            xSemaphoreTake(xSemaphore, portMAX_DELAY);
            printf("任务 vTaskLED 在运行\r\n");
            bsp_LedToggle(1);
            bsp_LedToggle(4);
            xSemaphoreGive(xSemaphore);/* vTaskDelayUntil 是绝对延迟,vTaskDelay 是相对延迟。*/
            vTaskDelayUntil(&xLastWakeTime, xFrequency);
        }
    }
    // * 函 数 名: vTaskMsgPro
    // * 功能说明: 实现对串口的互斥访问
    // * 形 参: pvParameters 是在创建该任务时传递的形参
    // * 返 回 值: 无
    // * 优 先 级: 3
    static void vTaskMsgPro(void *pvParameters)
    {
        TickType_t xLastWakeTime;
        const TickType_t xFrequency = 300;   /* 获取当前的系统时间 */  
        xLastWakeTime = xTaskGetTickCount();
        while (1)
        {
            /* 通过二值信号量实现资源互斥访问,永久等待直到资源可用 */  
            xSemaphoreTake(xSemaphore, portMAX_DELAY);
            printf("任务 vTaskMsgPro 在运行\r\n");
            bsp_LedToggle(2);
            bsp_LedToggle(3);
            xSemaphoreGive(xSemaphore);
            /* vTaskDelayUntil 是绝对延迟,vTaskDelay 是相对延迟。*/  
            vTaskDelayUntil(&xLastWakeTime, xFrequency);
              
        }
    }

     

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

    1.2 优先级翻转问题

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

    创建 3 个任务 Task1,Task2 和 Task3,优先级分别为 3,2,1。也就是 Task1 的优先级最高。
    任务 Task1 和 Task3 互斥访问串口打印 printf,采用二值信号实现互斥访问。
    起初 Task3 通过二值信号量正在调用 printf,被任务 Task1 抢占,开始执行任务 Task1,也就是上图的起始位置。

    运行过程描述如下:
    任务 Task1 运行的过程需要调用函数 printf,发现任务 Task3 正在调用,任务 Task1 会被挂起,等待 Task3 释放函数 printf。
    在调度器的作用下,任务 Task3 得到运行,Task3 运行的过程中,由于任务 Task2 就绪,抢占了 Task3的运行。优先级翻转问题就出在这里了,从任务执行的现象上看,任务 Task1 需要等待 Task2 执行完毕才有机会得到执行,这个与抢占式调度正好反了,正常情况下应该是高优先级任务抢占低优先级任务的执行,这里成了高优先级任务 Task1 等待低优先级任务 Task2 完成。所以这种情况被称之为优先级翻转问题。
    任务 Task2 执行完毕后,任务 Task3 恢复执行,Task3 释放互斥资源后,任务 Task1 得到互斥资源,从而可以继续执行。

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

    1.3 FreeRTOS 互斥信号量的实现

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

     

     

    运行条件:

    创建 2 个任务 Task1 和 Task2,优先级分别为 1 和 3,也就是任务 Task2 的优先级最高。
    任务 Task1 和 Task2 互斥访问串口打印 printf。
    使用 FreeRTOS 的互斥信号量实现串口打印 printf 的互斥访问。

    运行过程描述如下:

    低优先级任务 Task1 执行过程中先获得互斥资源 printf 的执行。此时任务 Task2 抢占了任务 Task1的执行,任务 Task1 被挂起。任务 Task2 得到执行。
    任务 Task2 执行过程中也需要调用互斥资源,但是发现任务 Task1 正在访问,此时任务 Task1 的优先级会被提升到与 Task2 同一个优先级,也就是优先级 3,这个就是所谓的优先级继承(Priority inheritance),这样就有效地防止了优先级翻转问题。任务 Task2 被挂起,任务 Task1 有新的优先级继续执行。
    任务 Task1 执行完毕并释放互斥资源后,优先级恢复到原来的水平。由于互斥资源可以使用,任务Task2 获得互斥资源后开始执行。

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

    1.4 FreeRTOS 中断方式互斥信号量的实现

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

    2 互 斥 信 号 量 API 函 数

    使用如下 18 个函数可以实现 FreeRTOS 的信号量(含计数信号量,二值信号量和互斥信号):
     xSemaphoreCreateBinary()
     xSemaphoreCreateBinaryStatic()
     vSemaphoreCreateBinary()
     xSemaphoreCreateCounting()
     xSemaphoreCreateCountingStatic()
     xSemaphoreCreateMutex()
     xSemaphoreCreateMutexStatic()
     xSem'CreateRecursiveMutex()
     xSem'CreateRecursiveMutexStatic()
     vSemaphoreDelete()
     xSemaphoreGetMutexHolder()
     uxSemaphoreGetCount()
     xSemaphoreTake()
     xSemaphoreTakeFromISR()
     xSemaphoreTakeRecursive()
     xSemaphoreGive()
     xSemaphoreGiveRecursive()

      xSemaphoreGiveFromISR()

    关于这 18 个函数的讲解及其使用方法可以看 FreeRTOS 在线版手册:

     

    2.1 函数 xSemaphoreCreateMutex

    函数原型:
    SemaphoreHandle_t xSemaphoreCreateMutex( void )
    函数描述:
    函数 xSemaphoreCreateMutex 用于创建互斥信号量。
    返回值,如果创建成功会返回互斥信号量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不足,无法为此互斥信号量提供所需的空间会返回 NULL。 

    使用这个函数要注意以下问题:
    1. 此函数是基于函数 xQueueCreateMutex 实现的:
    #define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
    函数 xQueueCreateMutex 的实现是基于消息队列函数 xQueueGenericCreate 实现的。
    2. 使用此函数要在 FreeRTOSConfig.h 文件中使能宏定义:
    #define configUSE_MUTEXES 1

    使用举例:

    // * 函 数 名: AppObjCreate
    // * 功能说明: 创建任务通信机制
    // * 形 参: 无
    // * 返 回 值: 无
    static void AppObjCreate (void)
    {
        /* 创建互斥信号量 */xMutex = xSemaphoreCreateMutex();
        if (xSemaphore == NULL)
        {/* 没有创建成功,用户可以在这里加入创建失败的处理机制 */}
    }
    

    2.2 函数 xSemaphoreGive

    函数原型:
    xSemaphoreGive( SemaphoreHandle_t xSemaphore ); /* 信号量句柄 */
    函数描述:
    函数 xSemaphoreGive 用于在任务代码中释放信号量。
    第 1 个参数是信号量句柄。
    返回值,如果信号量释放成功返回 pdTRUE,否则返回 pdFALSE,因为信号量的实现是基于消息队列,返回失败的主要原因是消息队列已经满了。
    使用这个函数要注意以下问题:
    1. 此函数是基于消息队列函数 xQueueGenericSend 实现的:
    #define xSemaphoreGive( xSemaphore )  xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
    2. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xSemaphoreGiveFromISR。

    3. 使用此函数前,一定要保证用函数 xSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或者 xSemaphoreCreateCounting()创建了信号量。
    4. 此函数不支持使用 xSemaphoreCreateRecursiveMutex()创建的信号量。 

    2.3 函数 xSemaphoreTake

    函数原型:
    xSemaphoreTake( SemaphoreHandle_t xSemaphore, /* 信号量句柄 */
    TickType_t xTicksToWait ); /* 等待信号量可用的最大等待时间 */
    函数描述:
    函数 xSemaphoreTake 用于在任务代码中获取信号量。
    第 1 个参数是信号量句柄。
    第 2 个参数是没有信号量可用时,等待信号量可用的最大等待时间,单位系统时钟节拍。

    返回值,如果创建成功会获取信号量返回 pdTRUE,否则返回 pdFALSE。
    使用这个函数要注意以下问题:
    1. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序使用的是xSemaphoreTakeFromISR。
    2. 如果消息队列为空且第 2 个参数为 0,那么此函数会立即返回。
    3. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第 2 个参数配置为 portMAX_DELAY,那么此函数会永久等待直到信号量可用。

    使用举例:

    static void vTaskLED(void *pvParameters)
    {
        TickType_t xLastWakeTime; const TickType_t xFrequency = 200;/* 获取当前的系统时间 */
        xLastWakeTime = xTaskGetTickCount();
        while (1)
        {
            /* 互斥信号量,xSemaphoreTake 和 xSemaphoreGive 一定要成对的调用 */
            xSemaphoreTake(xMutex, portMAX_DELAY);
            printf("任务 vTaskLED 在运行\r\n");
            bsp_LedToggle(2);
            bsp_LedToggle(3);
            xSemaphoreGive(xMutex);/* vTaskDelayUntil 是绝对延迟,vTaskDelay 是相对延迟。*/
            vTaskDelayUntil(&xLastWakeTime, xFrequency);
        }
    }

    互斥信号量,xSemaphoreTake 和 xSemaphoreGive 一定要成对的调用 

    经过测试,互斥信号量是可以被其他任务释放的,但是我们最好不要这么做,因为官方推荐的就是在同一个任务中接收和释放。如果在其他任务释放,不仅仅会让代码整体逻辑变得复杂,还会给使用和维护这套API的人带来困难。遵守规范,总是好的。

    裸机编程的时候,我经常想一个问题,就是怎么做到当一个标志位触发的时候,立即执行某个操作,如同实现标志中断一样,在os编程之后,我们就可以让一个优先级最高任务一直等待某个信号量,如果获得信号量,就执行某个操作,实现类似标志位中断的作用(当然,要想正真做到中断效果,那就需要屏蔽所有可屏蔽中断,而临界区就可以做到)。

    再说一下递归互斥信号量:递归互斥信号量,其实就是互斥信号量里面嵌套互斥信号量

    使用举例:

    static void vTaskMsgPro(void *pvParameters)
    {
        TickType_t xLastWakeTime;
        const TickType_t xFrequency = 1500;    /* 获取当前的系统时间 */
        xLastWakeTime = xTaskGetTickCount();
        while (1)
        {
            /* 递归互斥信号量,其实就是互斥信号量里面嵌套互斥信号量 */
            xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);
            {
                //假如这里是被保护的资源,第1层被保护的资源,用户可以在这里添加被保护资源
                printf("任务vTaskMsgPro在运行,第1层被保护的资源,用户可以在这里添加被保护资源\r\n");
                /* 第1层被保护的资源里面嵌套被保护的资源 */
                xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);
                {
    //假如这里是被保护的资源,第2层被保护的资源,用户可以在这里添加被保护资源
                    printf("任务vTaskMsgPro在运行,第2层被保护的资源,用户可以在这里添加被保护资源\r\n");
                    /* 第2层被保护的资源里面嵌套被保护的资源 */
                    xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);
                    {
                        printf("任务vTaskMsgPro在运行,第3层被保护的资源,用户可以在这里添加被保护资源\r\n");
                        bsp_LedToggle(1);
                        bsp_LedToggle(4);
                    }
                    xSemaphoreGiveRecursive(xRecursiveMutex);
                }
                xSemaphoreGiveRecursive(xRecursiveMutex);
            }
            xSemaphoreGiveRecursive(xRecursiveMutex);
            /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
            vTaskDelayUntil(&xLastWakeTime, xFrequency);
        }
    }

    可以前面的那个官方文档那样用if判断传递共享量:

    也可以用我们递归互斥信号量里面的portMAX_DELAY指定永久等待,即xSemaphoreTakeRecursive返回的不是pdTRUE时,会一直等待信号量,直到有信号量来才执行后面的语句。

    展开全文
  • 为了解决这个问题,引出了互斥信号量的概念。 1:信号量   信号量是一种上锁机制,该任务必须获得相应的钥匙才能进入,直至代码执行结束,释放钥匙,程序才退出。信号量分两种,一种是二进制信号量,一种是计数型
  • 信号量和互斥信号量的理解

    千次阅读 2019-04-09 08:26:11
    信号量和互斥信号量的记住和理解应用是不一样的哦,面试常问。 做下本人理解和参考他人后的笔记 对于互斥锁(Mutex)来说,只要有线程占有了该资源,那么不好意思,其他线程就是优先级再高,您也得等着,等我用完...
  • 【UCOSIII】UCOSIII的互斥信号量

    万次阅读 2018-07-02 21:18:54
    信号量用于控制对共享资源的保护,但是现在基本用来做任务同步用(不太清楚的可以参考链接:【UCOSIII】UCOSIII的信号量)。   优先级反转 优先级反转在可剥夺内核中是非常常见的,在实时系统中不允许出现这种...
  • 互斥信号量概念及其应用: 一,互斥信号定义: 在要被抢走时,把低优先级任务抬高。这样就解决了优先级翻转问题。 二值信号量用于信号同步: 互斥锁: 二,FreeRTOS互斥信号量介绍: 互斥信号量包括:普通互斥...
  • 互斥信号量和二进制信号量的区别 互斥量用于线程的互斥,信号量用于线程的同步。这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。 互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性...
  • 文章目录1 互斥信号量的原理与创建1.1 问题概述1.2 设计原理1.3 设计实现 1 互斥信号量的原理与创建 1.1 问题概述 问题:如何处理多个任务共享资源冲突的问题? 使用计数信号量存在的不足: 优先级反转现象: 1.2...
  • 互斥信号量的初值一般设为1 2、用途 (1)同步信号量的用途:防止被抢占 初始为空 低优先级的任务持有信号量,高优先级的任务需要这个信号量,只有当低优先级的任务give(释放)信号量,高优先级的任务才能take...
  • 1.互斥信号量 互斥信号量的申请与释放是要在同一个任务中进行的,不能在一个任务中申请而在另一个任务中释放。 互斥信号量主要解决的是,我在用的时候,别人都不能用。举个例子,我在像一段内存中写数据的时候,不...
  • 11、UCOSIII信号量和互斥信号量

    千次阅读 2018-08-30 15:14:40
    1、 信号量 1.1 信号量简介 ①信号量像是一种上锁机制,代码必须获得对应的钥匙才能继续执行,一旦获得了钥匙,也就意味着该任务具有进入被锁部分代码的权限。一旦执行至被锁代码段,则任务一直等待,直到对应被...
  • UCOS-ii中的互斥信号量-优先级反转

    千次阅读 2017-12-26 23:11:17
    二值信号量主要用于进行共享资源的独占式...互斥信号量一般用于降解优先级反转,优先级反转就是高优先级的任务的优先级被拉低了。具体如下: 我们有三个任务Task1,Task2,Task3,三个任务的优先级依次降低。 void
  • 互斥信号量作用:任务通过OSMutexPend()函数获得互斥信号量,如果互斥信号有效(不为0)则继续运行,否则进入等待。 那么他们之间区别在哪? 假设有三个任务A,B,C,他们的优先级分别为10、20、30,而任务A和C共同...
  • 在前一篇文章基础背景下,我们为了解决对共享资源访问出现线程冲突的问题,引入了几个概念,分别是计数型信号量互斥信号量。接下来我会根据自己的理解为大家一一进行讲解。 首先,什么叫做信号量? 还记不记得...
  • 简析互斥信号量的使用基础。 1、头文件声明 #include <stdio.h> #include <conio.h> #include "FreeRTOS.h" #include "task.h" #include "semphr.h" ...
  • ucosiii之互斥信号量

    2019-04-03 16:48:53
    在ucosiii中优秀级反转是非常常见的事,如:一个低优先级的任务...为了防止优先级反转,ucosiii支持一种特殊的二进制信号量:互斥信号量(解决优先级反转问题)如下图所示: //互斥信号量常用函数如下图所示 : ...
  • 互斥信号量实际解决方法就是,将低优先级任务D的优先级临时提升至任务A同一优先级,等信号量释放后,再将任务D的优先级恢复,这样任务切换只在这两个任务之间执行(如果有比A更高的还是会切换)。 API ...
  • FreeRTOS-互斥信号量

    千次阅读 2017-10-08 11:41:05
    0.前言  在嵌入式操作系统中互斥信号量是任务间资源保护的重要手段。下面结合一个具体例子说明FreeRTOS中的互斥信号量如何使用。... 互斥信号量的使用方法...在多数情况下,互斥信号量和二值型信号非常
  • **信号量的主要目的有两个:共享资源访问。... 在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这
  • C++ 多线程中互斥信号量的使用

    千次阅读 2017-07-09 23:10:08
    互斥信号量mutex 互斥量使用示例互斥信号量mutex互斥信号量是一种内核对象,它用来保证一个线程独占一个资源的访问。在使用互斥信号量的时候,有四个主要的函数: (1)CreateMutex,创建互斥信号量。函数原型如下...
  • 在可剥夺性的内核中,当任务以独占方式... 我们假设有三个任务a,b,c,a优先级高于b,b优先级高于c,a和c都需要访问一个共享资源s,保护该资源的信号量为互斥信号量, 假设当前任务c申请了信号量访问s,还没有释放,此时...
  • 实际应用中, 可能会是去 open 一个 I2C 设备节点, 这个 I2C 控制着一个 I2C 多路分配器(例如1分8) 8路 I2C 连接着8个相同的 ...为了多进程共同访问这个 I2C 资源, 就要用信号量做同步和互斥了 // semaphore.c #in...
  • 在可剥夺的内核中,当任务独占式使用共享资源的时候,会出现低优先级的任务先于高优先级任务运行的现象,这个现象被称为优先级反转,为了解决优先级反转这个问题,UCOSIII引入了互斥信号量这个概念。本章,就来讲解...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 90,575
精华内容 36,230
关键字:

互斥信号量