精华内容
下载资源
问答
  • 互斥信号量与信号量的理解

    万次阅读 多人点赞 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 分别提供了二值信号量和计数信号量,其中二值信号量可以理解成计数信号量的一种特殊形式,即初始化为仅有一个资源可以使用,只不过 FreeRTOS 对这两种都提供了 API函数,而像 RTX,uCOS-II 和 III 是仅提供...

    本章节讲解 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时,会一直等待信号量,直到有信号量来才执行后面的语句。

    展开全文
  • 一、互斥信号量简介 ...互斥信号量使用和二值信号量相同的API操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性。当一个互斥信号量正在被一个低优先

    一、互斥信号量简介

    互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。互斥信号量使用和二值信号量相同的API操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的"优先级翻转"的影响降到最低。优先级继承并不能完全的消除优先级翻转,它只是尽可能的降低优先级翻转带来的影响。硬实时应用应该在设计之初就要避免优先级翻转的发生。互斥信号量不能用于中断服务函数中,原因如下:
    1、互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。
    2、中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

    二、互斥信号量相关API

    FreeRTOS提供了两个互斥信号量创建函数,如下表所示:

    函数描述
    xSemaphoreCreateMutex()使用动态方法创建互斥信号量
    xSemaphoreCreateMutexStatic()使用静态方法创建互斥信号量

    1、函数xSemaphoreCreateMutex()
    此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。此函数本质是一个宏,真正完成信号量创建的是函数xQueueCreateMutex(),此函数原型如下:

    SemaphoreHandle_t xSemaphoreCreateMutex(void)
    
    参数描述
    返回值NULL:互斥信号量创建失败。其他值:创建成功的互斥信号量的句柄。

    2、函数xSemaphoreCreateMutexStatic()
    此函数也是创建互斥信号量的,只不过使用此函数创建互斥信号量的话信号量所需要的,RAM需要由用户来分配,此函数是个宏,具体创建过程是通过函数xQueueCreateMutexStatic()来完成的,函数原型如下:

    SemaphoreHandle_t xSemaphoreCreateMutexStatic(StaticSemaphore_t*pxMutexBuffer)
    
    参数描述
    pxMutexBuffer此参数指向一个StaticSemaphore_t类型的变量,用来保存信号量结构体。
    返回值NULL:互斥信号量创建失败。其他值:创建成功的互斥信号量的句柄。

    三、互斥信号量相关函数详细分析

    1、互斥信号量创建过程分析
    动态创建互斥信号量函数xSemaphoreCreateMutex(),此函数是个宏,定义如下:

    #define xSemaphoreCreateMutex() xQueueCreateMutex(queueQUEUE_TYPE_MUTEX)
    

    可以看出,真正干事的是函数xQueueCreateMutex(),此函数在文件queue.c中有如下定义,

    QueueHandle_t xQueueCreateMutex( 
    const uint8_t ucQueueType )
    {
    Queue_t *pxNewQueue;
    const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;
    
    	pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );(1)
    	prvInitialiseMutex( pxNewQueue );(2)
    
    	return pxNewQueue;
    }
    
    • (1)、调用函数xQueueGenericCreate()创建一个队列,队列长度为1,队列项长度为0,队列类型为参数ucQueueType。由于本函数是创建互斥信号量的,所以参数ucQueueType为queueQUEUE_TYPE_MUTEX。
    • (2)、调用函数prvInitialiseMutex()初始化互斥信号量。

    其中prvInitialiseMutex()代码如下:

    static void prvInitialiseMutex( Queue_t *pxNewQueue )
    {
    	if( pxNewQueue != NULL )
    	{
    /*虽然创建队列的时候会初始化队列结构体的成员变量,但是现在是创建互斥信号量,有些成员需要重新赋值,特别是用于优先级继承的*/
    		pxNewQueue->pxMutexHolder = NULL;(1)
    		pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;(2)
    
    		//如果是递归互斥信号量
    		pxNewQueue->u.uxRecursiveCallCount = 0;(3)
    
    		traceCREATE_MUTEX( pxNewQueue );
    
    		//释放互斥信号量
    		( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );
    	}
    	else
    	{
    		traceCREATE_MUTEX_FAILED();
    	}
    }
    

    (1)和(2)、这里大家可能会疑惑,队列结构体Queue_t中没有pxMutexHolder和uxQueueType这两个成员变量?这两个东西是哪里来的妖孽?这两个其实是宏,专门为互斥信号量准备的,在文件queue.c中有如下定义:

    #define pxMutexHolder pcTail
    #define uxQueueType pcHead
    #define queueQUEUE_IS_MUTEX NULL
    

    当Queue_t用于表示队列的时候pcHead和pcTail指向队列的存储区域,当Queue_t用于表示互斥信号量的时候就不需要pcHead和pcTail了。当用于互斥信号量的时候将pcHead指向NULL来表示pcTail保存着互斥队列的所有者,pxMutexHolder指向拥有互斥信号量的那个任务的任务控制块。重命名pcTail和pcHead就是为了增强代码的可读性。
    (3)、如果创建的互斥信号量是互斥信号量的话,还需要初始化队列结构体中的成员变量u.uxRecursiveCallCount。
    互斥信号量创建成功以后会调用函xQueueGenericSend()释放一次信号量,说明互斥信号量默认就是有效的!互斥信号量初始化完成如下图所示:
    在这里插入图片描述
    2、释放互斥信号量过程分析
    释放互斥信号量的时候和二值信号量、计数型信号量一样,都是用的函数xSemaphoreGive()(实际上完成信号量释放的是函数xQueueGenericSend()。)不过由于互斥信号量涉及到优先级继承的问题,所以具体处理过程会有点区别。使用函数xSemaphoreGive()释放信号量最重要的一步就是将uxMessagesWaiting加一,而这一步就是通过函数
    prvCopyDataToQueue()来完成的,释放信号量的函数xQueueGenericSend()会调用prvCopyDataToQueue()。互斥信号量的优先级继承也是在函数prvCopyDataToQueue()中完成的,此函数中有如下一段代码:

    static BaseType_t prvCopyDataToQueue( 
    Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition )
    {
    BaseType_t xReturn = pdFALSE;
    UBaseType_t uxMessagesWaiting;
    	uxMessagesWaiting = pxQueue->uxMessagesWaiting;
    
    	if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
    	{
    	//互斥信号量
    		#if ( configUSE_MUTEXES == 1 )
    		{
    			if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )(1)
    			{
    				xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );(2)
    				pxQueue->pxMutexHolder = NULL;(3)
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    		#endif /* configUSE_MUTEXES */
    	}
    	/**********省略其他代码************/
    	
    
    • (1)、当前操作的是互斥信号量。
    • (2)、调用函数xTaskPriorityDisinherit()处理互斥信号量的优先级继承问题。
    • (3)、互斥信号量释放以后,互斥信号量就不属于任何任务了,所以pxMutexHolder要指向NULL。

    现在来看一下函数xTaskPriorityDisinherit()是怎么具体的处理优先级继承的,函数xTaskPriorityDisinherit0代码如下:

    BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder )
    {
    TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
    BaseType_t xReturn = pdFALSE;
    
    	if( pxMutexHolder != NULL )(1)
    	{
    		/* 当一个任务获取到互斥信号量就会涉及优先级继承的问题,正在释放互斥信号量的任务肯定是现在正在运行的任务pxCurrentTCB */
    		configASSERT( pxTCB == pxCurrentTCB );
    
    		configASSERT( pxTCB->uxMutexesHeld );
    		( pxTCB->uxMutexesHeld )--;(2)
    
    		/* 是否存在优先级继承?如果存在的话任务当前优先级肯定和任务基优先级不同*/
    		if( pxTCB->uxPriority != pxTCB->uxBasePriority )(3)
    		{
    			/* 当前任务只获取到一个互斥信号量 */
    			if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 )(4)
    			{
    				if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )(5)
    				{
    					taskRESET_READY_PRIORITY( pxTCB->uxPriority );(6)
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    
    				/* 使用新的优先级将任务重新添加到就绪列表中 */
    				traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );
    				pxTCB->uxPriority = pxTCB->uxBasePriority;(7)
    				listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority );(8) 
    				prvAddTaskToReadyList( pxTCB );(9)
    				xReturn = pdTRUE;(10)
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    		else
    		{
    			mtCOVERAGE_TEST_MARKER();
    		}
    	}
    	else
    	{
    		mtCOVERAGE_TEST_MARKER();
    	}
    
    	return xReturn;
    }
    
    • (1)、函数的参数pxMutexHolder表示拥有此互斥信号量任务控制块,所以要先判断此互斥信号量是否已经被其他任务获取。
    • (2)、有的任务可能会获取多个互斥信号量,所以就需要标记任务当前获取到的互斥信号量个数,任务控制块结构体的成员变量uxMutexesHeld用来保存当前任务获取到的互斥信号量个数。任务每释放一次互斥信号量,变量uxMutexesHeld肯定就要减一。
    • (3)、判断是否存在优先级继承,如果存在的话任务的当前优先级肯定不等于任务的基优先级。
    • (4)、判断当前释放的是不是任务所获取到的最后一个互斥信号量,因为如果任务还获取了其他互斥信号量的话就不能处理优先级继承。优先级继承的处理必须是在释放最后一个互斥信号量的时候。
    • (5)、优先级继承的处理说白了就是将任务的当前优先级降低到任务的基优先级,所以要把当前任务先从任务就绪表中移除。当任务优先级恢复为原来的优先级以后再重新加入到就绪表中。
    • (6)、如果任务继承来的这个优先级对应的就绪表中没有其他任务的话就将取消这个优先级的就绪态。
    • (7)、重新设置任务的优先级为任务的基优先级uxBasePriority。
    • (8)、复位任务的事件列表项。
    • (9)、将优先级恢复后的任务重新添加到任务就绪表中。
    • (10)、返回pdTRUE,表示需要进行任务调度。

    3、获取互斥信号量过程分析
    获取互斥信号量的函数同获取二值信号量和计数型信号量的函数相同,都是xSemaphoreTake()(实际执行信号量获取的函数是xQueueGenericReceive()),获取互斥信号量的过程也需要处理优先级继承的问题,函数xQueueGenericReceive()在文件queue.c中有定义,在缩减后的函数代码如下:

    BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, 
    void * const pvBuffer, TickType_t xTicksToWait, 
    const BaseType_t xJustPeeking )
    {
    BaseType_t xEntryTimeSet = pdFALSE;
    TimeOut_t xTimeOut;
    int8_t *pcOriginalReadPosition;
    Queue_t * const pxQueue = ( Queue_t * ) xQueue;
    	for( ;; )
    	{
    		taskENTER_CRITICAL();
    		{
    			const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;
    
    		//判断队列是否有消息
    			if( uxMessagesWaiting > ( UBaseType_t ) 0 )(1)
    			{
    				pcOriginalReadPosition = pxQueue->u.pcReadFrom;
    
    				prvCopyDataFromQueue( pxQueue, pvBuffer );(2)
    
    				if( xJustPeeking == pdFALSE )(3)
    				{
    					traceQUEUE_RECEIVE( pxQueue );
    
    					//移除消息
    					pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;(4)
    
    					#if ( configUSE_MUTEXES == 1 )(5)
    					{
    						if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
    						{
    							pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); (6)
    						}
    						else
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    					}
    					#endif /* configUSE_MUTEXES */
    /*查看是否有任务因为入队而阻塞,如果有的话就需要解除阻塞态。*/
    					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )(7)
    					{
    						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
    						{
    /*如果解除任务阻塞的任务优先级比当前任务优先级高的话就需要进行一次任务切换*/
    							queueYIELD_IF_USING_PREEMPTION();
    						}
    						else
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    				}
    				else(8)
    				{
    					traceQUEUE_PEEK( pxQueue );
    //读取队列中的消息以后需要删除消息
    					pxQueue->u.pcReadFrom = pcOriginalReadPosition;
    //如果有任务因为出队而阻塞的话就解除任务的阻塞态
    					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )(9)
    					{
    						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
    						{
    //如果解除阻塞的任务优先级比当前任务优先级高的话就需要进行一次任务切换
    							queueYIELD_IF_USING_PREEMPTION();
    						}
    						else
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    				}
    
    				taskEXIT_CRITICAL();
    				return pdPASS;
    			}
    			else //队列为空 (10)
    			{
    				if( xTicksToWait == ( TickType_t ) 0 )
    				{
    	/*队列为空,如果阻塞时间为0的话就直接返回errQUEUE_EMPTY*/
    					taskEXIT_CRITICAL();
    					traceQUEUE_RECEIVE_FAILED( pxQueue );
    					return errQUEUE_EMPTY;
    				}
    				else if( xEntryTimeSet == pdFALSE )
    				{
    	//队列为空且设置了阻塞时间,需要初始化时间状态结构体。
    					vTaskSetTimeOutState( &xTimeOut );
    					xEntryTimeSet = pdTRUE;
    				}
    				else
    				{
    					/* Entry time was already set. */
    					mtCOVERAGE_TEST_MARKER();
    				}
    			}
    		}
    		taskEXIT_CRITICAL();
    		vTaskSuspendAll();
    		prvLockQueue( pxQueue );
    
    //更新时间状态结构体,并且检查超时是否发送。
    		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )(11)
    		{
    			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )(12)
    			{
    				traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
    
    				#if ( configUSE_MUTEXES == 1 )
    				{
    					if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )(13)
    					{
    						taskENTER_CRITICAL();
    						{
    							vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );(14)
    						}
    						taskEXIT_CRITICAL();
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    				}
    				#endif
    
    				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );(15)
    				prvUnlockQueue( pxQueue );
    				if( xTaskResumeAll() == pdFALSE )
    				{
    					portYIELD_WITHIN_API();
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    			}
    			else//再试一次
    			{
    				prvUnlockQueue( pxQueue );
    				( void ) xTaskResumeAll();
    			}
    		}
    		else
    		{
    			prvUnlockQueue( pxQueue );
    			( void ) xTaskResumeAll();
    
    			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
    			{
    				traceQUEUE_RECEIVE_FAILED( pxQueue );
    				return errQUEUE_EMPTY;
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    	}
    }
    
    • (1)、队列不为空,可以从队列中提取数据。
    • (2)、调用函数prvCopyDataFromQueue()使用数据拷贝的方式从队列中提取数据。
    • (3)、数据读取以后需要将数据删除掉。
    • (4)、队列的消息数量计数器uxMessagesWaiting减一,通过这一步就将数据删除掉了。
    • (5)、表示此函数是用于获取互斥信号量的。
    • (6)、获取互斥信号量成功,需要标记互斥信号量的所有者,也就是给pxMutexHolder赋值,pxMutexHolder应该是当前任务的任务控制块。但是这里是通过函数pvTaskIncrementMutexHeldCount()来赋值的,此函数很简单,只是将任务控制块中的成员变量uxMutexesHeld加一,表示任务获取到了一个互斥信号量,最后此函数返回当前任务的任务控制块。
    • (7)、出队成功以后判断是否有任务因为入队而阻塞的,如果有的话就需要解除任务的阻塞态,如果解除阻塞的任务优先级比当前任务的优先级高还需要进行一次任务切换。
    • (8)、出队的时候不需要删除消息。
    • (9)、如果出队的时候不需要删除消息的话那就相当于刚刚出队的那条消息接着有效!既然还有有效的消息存在队列中,那么就判断是否有任务因为出队而阻塞,如果有的话就解除任务的阻塞态。同样的,如果解除阻塞的任务优先级比当前任务的优先级高的话还需要进行一次任务切换。
    • (10)、上面分析的都是队列不为空的时候,那当队列为空的时候该如何处理呢?处理过程和队列的任务级通用入队函数xQueueGenericSend()类似。如果阻塞时间为0的话就就直接返回errQUEUE_EMPTY,表示队列空,如果设置了阻塞时间的话就进行相关的处理。
    • (11)、检查超时是否发生,如果没有的话就需要将任务添加到队列的xTasksWaitingToReceive列表中。
    • (12)、检查队列是否继续为空?如果不为空的话就会在重试一次出队。
    • (13)、表示此函数是用于获取互斥信号量的。
    • (14)、调用函数vTaskPriorityInherit()处理互斥信号量中的优先级继承问题,如果函数xQueueGenericReceive()用于获取互斥信号量的话,此函数执行到这里说明互斥信号量正在被其他的任务占用。此函数和函数xTaskPriorityDisinherit()过程相反。此函数会判断当前任务的任务优先级是否比正在拥有互斥信号量的那个任务的任务优先级高,如果是的话就会把拥有互斥信号量的那个低优先级任务的优先级调整为与当前任务相同的优先级
    • (15)、经过(12)步判断,队列依旧为空,那么就将任务添加到列表xTasksWaitingToReceive中。

    互斥信号量的讲解就到这里啦!!!谢谢大家收看!!!

    展开全文
  • FreeRTOS互斥信号量

    2021-01-14 14:04:32
    FreeRTOS互斥信号量本文完整版地址:...注意,建议初学者学习完前两个章节的信号量后再学习本章节的互斥信号量。FreeRTOS中互斥信号量的源码实现是基于消息队列实现的。本章教程配套的例子...

    FreeRTOS互斥信号量

    本文完整版地址:http://http://bbs.armfly.com/read.php?tid=21381

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

    FreeRTOS中互斥信号量的源码实现是基于消息队列实现的。

    本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。

    23.1 互斥信号量

    23.2 互斥信号量API函数

    23.3 实验例程说明

    23.4 总结

    23.1互斥信号量

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

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

    运行条件:

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

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

    代码实现:

    u 创建二值信号量

    static SemaphoreHandle_t  xSemaphore = NULL;

    static void AppObjCreate (void)

    {

    xSemaphore = xSemaphoreCreateBinary();

    if(xSemaphore == NULL)

    {

    }

    xSemaphoreGive(xSemaphore);

    }

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

    static void vTaskLED(void *pvParameters)

    {

    TickType_t xLastWakeTime;

    const TickType_t xFrequency = 300;

    xLastWakeTime = xTaskGetTickCount();

    while(1)

    {

    xSemaphoreTake(xSemaphore, portMAX_DELAY);

    printf("任务vTaskLED在运行rn");

    bsp_LedToggle(1);

    bsp_LedToggle(4);

    xSemaphoreGive(xSemaphore);

    vTaskDelayUntil(&xLastWakeTime, xFrequency);

    }

    }

    static void vTaskMsgPro(void *pvParameters)

    {

    TickType_t xLastWakeTime;

    const TickType_t xFrequency = 300;

    xLastWakeTime = xTaskGetTickCount();

    while(1)

    {

    xSemaphoreTake(xSemaphore, portMAX_DELAY);

    printf("任务vTaskMsgPro在运行rn");

    bsp_LedToggle(2);

    bsp_LedToggle(3);

    xSemaphoreGive(xSemaphore);

    vTaskDelayUntil(&xLastWakeTime, xFrequency);

    }

    }

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

    23.1.2优先级翻转问题

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

     

    运行条件:

    u创建3个任务Task1,Task2和Task3,优先级分别为3,2,1。也就是Task1的优先级最高。

    u任务Task1和Task3互斥访问串口打印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得到互斥资源,从而可以继续执行。

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

    23.1.3FreeRTOS互斥信号量的实现

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

    运行条件:

    u创建2个任务Task1和Task2,优先级分别为1和3,也就是任务Task2的优先级最高。

    u任务Task1和Task2互斥访问串口打印printf。

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

    运行过程描述如下:

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

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

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

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

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

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

    23.2互斥信号量API函数

    使用如下18个函数可以实现FreeRTOS的信号量(含计数信号量,二值信号量和互斥信号):

    (1)     xSemaphoreCreateBinary()

    (2)    xSemaphoreCreateBinaryStatic()

    (3)    vSemaphoreCreateBinary()

    (4)    xSemaphoreCreateCounting()

    (5)    xSemaphoreCreateCountingStatic()

    (6)    xSemaphoreCreateMutex()

    (7)    xSemaphoreCreateMutexStatic()

    (8)    xSem"CreateRecursiveMutex()

    (9)    xSem"CreateRecursiveMutexStatic()

    (10)    vSemaphoreDelete()

    (11)    xSemaphoreGetMutexHolder()

    (12)    uxSemaphoreGetCount()

    (13)    xSemaphoreTake()

    (14)    xSemaphoreTakeFromISR()

    (15)    xSemaphoreTakeRecursive()

    (16)    xSemaphoreGive()

    (17)    xSemaphoreGiveRecursive()

    (18)    xSemaphoreGiveFromISR()

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

    上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:

    #define tskBLOCKED_CHAR         ( "B" )任务阻塞

    #define tskREADY_CHAR         ( "R" )任务就绪

    #define tskDELETED_CHAR          ( "D" )任务删除

    #define tskSUSPENDED_CHAR  ( "S" )任务挂起

    程序设计:

    u任务栈大小分配:

    vTaskUserIF任务:2048字节

    vTaskLED任务:2048字节

    vTaskMsgPro任务 :2048字节

    vTaskStart任务:2048字节

    任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的

    #define configTOTAL_HEAP_SIZE       ( ( size_t ) ( 17 * 1024 ) )

    u系统栈大小分配:

    uFreeROTS初始化:

    int main(void)

    {

    __set_PRIMASK(1);

    bsp_Init();

    vSetupSysInfoTest();

    AppTaskCreate();

    AppObjCreate();

    vTaskStartScheduler();

    while(1);

    }

    u硬件外设初始化

    硬件外设的初始化是在bsp.c文件实现:

    void bsp_Init(void)

    {

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

    bsp_InitUart();

    bsp_InitLed();

    bsp_InitKey();

    }

    uFreeRTOS任务创建:

    static void AppTaskCreate (void)

    {

    xTaskCreate( vTaskTaskUserIF,

    "vTaskUserIF",

    512,

    NULL,

    1,

    &xHandleTaskUserIF ); /*任务句柄*/

    xTaskCreate( vTaskLED,

    "vTaskLED",

    512,

    NULL,

    2,

    &xHandleTaskLED );

    xTaskCreate( vTaskMsgPro,

    "vTaskMsgPro",

    512,

    NULL,

    3,

    &xHandleTaskMsgPro ); /*任务句柄*/

    xTaskCreate( vTaskStart,

    "vTaskStart",

    512,

    NULL,

    4,

    &xHandleTaskStart );

    }

    uFreeRTOS互斥信号量创建:

    static void AppObjCreate (void)

    {

    xMutex = xSemaphoreCreateMutex();

    if(xMutex == NULL)

    {

    }

    }

    u四个FreeRTOS任务的实现:

    static void vTaskTaskUserIF(void *pvParameters)

    {

    uint8_t ucKeyCode;

    uint8_t pcWriteBuffer[500];

    while(1)

    {

    ucKeyCode = bsp_GetKey();

    if (ucKeyCode != KEY_NONE)

    {

    switch (ucKeyCode)

    {

    case KEY_DOWN_K1:

    xSemaphoreTake(xMutex, portMAX_DELAY);

    printf("=================================================rn");

    printf("任务名任务状态 优先级剩余栈 任务序号rn");

    vTaskList((char *)&pcWriteBuffer);

    printf("%srn", pcWriteBuffer);

    printf("rn任务名运行计数使用率rn");

    vTaskGetRunTimeStats((char *)&pcWriteBuffer);

    printf("%srn", pcWriteBuffer);

    xSemaphoreGive(xMutex);

    break;

    default:

    break;

    }

    }

    vTaskDelay(20);

    }

    }

    static void vTaskLED(void *pvParameters)

    {

    TickType_t xLastWakeTime;

    const TickType_t xFrequency = 200;

    xLastWakeTime = xTaskGetTickCount();

    while(1)

    {

    xSemaphoreTake(xMutex, portMAX_DELAY);

    printf("任务vTaskLED在运行rn");

    bsp_LedToggle(2);

    bsp_LedToggle(3);

    xSemaphoreGive(xMutex);

    vTaskDelayUntil(&xLastWakeTime, xFrequency);

    }

    }

    static void vTaskMsgPro(void *pvParameters)

    {

    TickType_t xLastWakeTime;

    const TickType_t xFrequency = 300;

    xLastWakeTime = xTaskGetTickCount();

    while(1)

    {

    xSemaphoreTake(xMutex, portMAX_DELAY);

    printf("任务vTaskMsgPro在运行rn");

    bsp_LedToggle(1);

    bsp_LedToggle(4);

    xSemaphoreGive(xMutex);

    vTaskDelayUntil(&xLastWakeTime, xFrequency);

    }

    }

    static void vTaskStart(void *pvParameters)

    {

    while(1)

    {

    bsp_KeyScan();

    vTaskDelay(10);

    }

    }

    FreeRTOS互斥信号量相关教程

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

    千次阅读 2019-08-14 13:47:32
    互斥信号量可以在资源保护的时候很有帮助。用于控制在两个或多个任务间访问共享资源。任务1里面用互斥,那么任务2只能等任务1访问完再访问同一个变量。 比如全局变量double gADC_value[CH_ADC_NUM] = {0}; //7 adc ...
  • 互斥信号量概念及其应用: 一,互斥信号定义: 在要被抢走时,把低优先级任务抬高。这样就解决了优先级翻转问题。 二值信号量用于信号同步: 互斥锁: 二,FreeRTOS互斥信号量介绍: 互斥信号量包括:普通互斥...
  • 互斥量和信号量的理解

    千次阅读 2018-07-29 15:59:56
    互斥量表现互斥现象的数据结构,也被当作二元信号灯。一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源。   Mutex本质上说...
  • 任务 Task1 运行的过程需要调用函数 printf,发现任务 ... 任务 Task2 执行完毕后,任务 Task3 恢复执行,Task3 释放互斥资源后,任务 Task1 得到互斥资源,从而可以继续执行。上面就是一 个产生优先级翻转问题的现象。
  • 当一个互斥信号量被一个低任务使用,而此时高任务也想获取这个信号量,高任务就会被阻塞,不过高任务会将低任务提升到与自己相同的优先级。 优先级继承尽可能的降低了高任务处于阻塞太的时间,并将“优先级反转”的...
  • 互斥信号量的初值一般设为1 2、用途 (1)同步信号量的用途:防止被抢占 初始为空 低优先级的任务持有信号量,高优先级的任务需要这个信号量,只有当低优先级的任务give(释放)信号量,高优先级的任务才能take...
  • 1.互斥信号量基本概念 互斥量又称互斥信号量(本质是信号量),是一种特殊的二值信号量。 任意时刻互斥量的状态只有两种,开锁或闭锁。 当互斥量被任务持有时,该互斥量处于闭锁状态,这个任务获得互斥量的所有权。...
  • 互斥信号量作用:任务通过OSMutexPend()函数获得互斥信号量,如果互斥信号有效(不为0)则继续运行,否则进入等待。 那么他们之间区别在哪? 假设有三个任务A,B,C,他们的优先级分别为10、20、30,而任务A和C共同...
  • 11、UCOSIII信号量和互斥信号量

    千次阅读 2018-08-30 15:14:40
    1、 信号量 1.1 信号量简介 ①信号量像是一种上锁机制,代码必须获得对应的钥匙才能继续执行,一旦获得了钥匙,也就意味着该任务具有进入被锁部分代码的权限。一旦执行至被锁代码段,则任务一直等待,直到对应被...
  • 在可剥夺的内核中,当任务独占式使用共享资源的时候,会出现低优先级的任务先于高优先级任务运行的现象,这个现象被称为优先级反转,为了解决优先级反转这个问题,UCOSIII引入了互斥信号量这个概念。本章,就来讲解...
  • 代码: #define THREAD_PRIORITY 6 //优先级 #define THREAD_STACK_SIZE 512 //栈大小 #define THREAD_TIMESLICE 5 //线程时间片 static rt_mutex_t dynamic_mutex = RT_NULL; //定义互斥量指针 static rt
  • 对于信号量就可以认为是一个仓库的操作概念,信号就是反馈一个通知罢了,我这个操作做完了,就给你一个信号,我操作了多少就是一个量的概念,这就是我理解的信号量 P操作 P操作就是从仓库里面拿货,如果仓库里面...
  • 互斥量理论实验一:互斥信号量基本使用调用函数创建互斥信号量打开宏开关创建任务实验仿真实验二:优先级反转实验实验描述实验仿真实验三:优先级继承实验仿真实验四:互斥量的缺陷(可由别人释放锁)实验概述实验...
  • UCOSIII信号量互斥

    2019-11-20 20:39:18
    在可剥夺的内核中,当任务独占式使用共享资源的时候,会出现低优先级的任务先于高优先级任务运行的现象,这个现象被称为优先级反转,为了解决优先级反转这个问题,UCOSIII 引入了互斥信号量这个概念。 信号量 信号量...
  • FreeRTOS — 互斥信号量

    2020-12-22 13:42:02
    1 、互 斥 信 号 量1.1 互斥信号量的概念及其作用互斥信号量的主要作用是对资源实现互斥访问,使用二值信号量也可以实现互斥访问的功能,不过互斥信号量与二值信号量有区别。下面我们先举一个通过二值信号量实现资源...
  • 昨天一开始看事件标志组的时候确实不知道怎么回事,后来百度一下,明白了事件标志组的作用 以后,再去看书上的讲解和原码就清晰多了,很容易就明白了他的基本运行机理。这也给了我一点启示,学一个东西,看一个东西...
  • 这一篇在上篇的基础上得以延伸,介绍一下相对复杂的进程互斥、同步的案例。 问题描述 系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并...
  • tid=99514 第20章 ThreadX互斥信号量 本章节讲解ThreadX的资源共享机制---互斥信号量(Mutex,即Mutual Exclusion的缩写)。注意,建议初学者学习完前...20.1.1 互斥信号量的概念及其作用 20.1.2 优先级翻转问题...
  • 二值信号量和互斥信号量的区别

    千次阅读 2014-06-30 16:00:23
    互斥信号量和二进制信号量的区别   互斥型信号量必须是同一个任务申请,同一个任务释放,其他任务释放无效。同一个任务可以递归申请。    二进制信号量,一个任务申请成功后,可以由另一个任务释放。  ...
  • 信号量---实现同步互斥

    千次阅读 2018-12-14 18:27:12
    了解信号量之前,要先了解临界资源、同步与互斥的概念 1.临界资源:在同一时间只能被一个进程调用的资源,也称互斥资源。 2.同步:保证访问的时序可控性,使调用资源的的顺序合理。 3.互斥:在进程调用临界资源是...
  • 昨天一开始看事件标志组的时候确实不知道怎么回事,后来百度一下,明白了事件标志组的作用以后,再去看书上的讲解和原码就清晰多了,很容易就明白了他的基本运行机理。这也给了我一点启示,学一个东西,看一个东西...
  • FreeRTOS 队列 信号量 互斥

    千次阅读 2019-12-03 23:41:30
    文章目录前言Queue 队列semaphore 信号量Mutex 互斥量微信公众号 前言 FreeRTOS STM32CubeMX配置 内存管理 任务管理 上节介绍了用STM32CubeMX生成带FreeRTOS的工程, 细心的同学可能发现, 已创建任务的函数为例, ...
  • 互斥量和信号量的区别

    万次阅读 多人点赞 2018-04-25 23:36:11
    互斥量和信号量的区别1. 互斥量用于线程的互斥信号量用于线程的同步。这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 41,957
精华内容 16,782
关键字:

互斥信号量的作用