FreeRTOS 实时操作系统第十三讲 - 互斥信号量

一、信号量的概念

1、信号量的基本概念

  消息队列是实现任务与任务或任务与中断间通信的数据结构,可类比裸机编程中的数组

  信号量是实现任务与任务或任务与中断间通信的机制,可以类比裸机编程中的标志位

  信号量 (semaphore) 可以实现任务与任务或任务与中断间的同步功能 (二值信号量)、资源管理(计数信号量)、临界资源的互斥访问(互斥信号量) 等

  信号量是一个非负正数,二值信号量与互斥信号量取值范围为 0-1,计数信号量取值范围是 0-N(N>1)

  0: 信号量为空,所有试图获取它的任务都将处于阻塞状态,直到超时退出或其他任务释放信号量

  正数: 表示有一个或多个信号量供获取

2、信号量的分类

  1. 二值信号量 (重点讲解同步应用)
  2. 计数信号量 (重点讲解资源管理)
  3. 互斥信号量 (重点讲解互斥访问)
  4. 递归互斥信号量 (简要了解即可)

二、互斥信号量的定义与应用

1、互斥信号量的定义

  前面学过,取值只有 0 与 1 两种状态的信号量称之为二值信号量。 而互斥信号量是一种特殊的二值信号量,具有防止优先级翻转的特性。

  创建互斥信号量时,系统会为创建的互斥信号量分配内存,互斥信号量创建完成后的示意图如下:

  从上图可以看出,互斥信号量是一种长度为 1,消息大小为 0 的特殊消息队列。

    因为这个队列只有空或满两种状态,而且消息大小为 0,因此在运用时,只需要知道队列中是否有消息即可,而无需关注消息是什么。

2、互斥信号量的应用

    在嵌入式操作系统中,互斥信号量用于临界资源的独占式访问,只能用于任务与任务间,因为其特有的优先级继承机制只能在任务中起作用,在中断的上下文环境毫无意义。

应用场景:

  比如有两个任务需要通过同一串口发送数据,其硬件资源只有一个,那么两个任务不能同时发送,否则会导致数据错误。此时就可以用互斥信号量对串口资源进行保护,当任务 1 正在使用串口发送数据时,互斥信号量变为无效,任务 2 无法使用串口,任务 2 必须等待互斥信号量有效 (任务 1 释放信号量),才能获得串口使用权,进而发送数据。

3、简要了解递归互斥信号量

  递归互斥信号量是一种特殊的互斥信号量,支持拥有该信号量使用权的任务重复多次获取,而不会死锁。

  任务成功获取几次递归互斥信号量,就要返还几次,在此之前,递归互斥信号量都处于无效状态。

  递归互斥信号量应用很少,简要了解即可。

三、优先级翻转问题

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

运行条件:

  • 创建 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 之间的任务特别多,对于抢占式实时操作系统来说是致命的危害,有可能导致系统崩溃 ,下一讲通过编程进行验证。

  不过也不用担心,使用互斥信号量,可以有效的防止优先级翻转问题,后面会讲解。

四、优先级翻转编程测试

视频讲解

串口输出信息:

五、互斥信号量的运作机制

  互斥量处理不同任务对临界资源的访问时,任务要获得互斥量才能进行资源访问。一旦有任务成功获得了互斥量,则互斥量立即变为闭锁状态,此时其他任务会因为获取不到互斥量而不能访问这个资源。任务会根据用户自定义的等待时间进行等待,直到互斥量被持有的任务释放,其他任务才能获取互斥量从而得以访问该临界资源。此时互斥量再次上锁,如此一来就可以确保每个时刻只有一个任务正在访问这个临界资源,保证了临界资源操作的安全性,具体如下图所示。

  ①:因为互斥量具有优先级继承机制,一般选择使用互斥量对资源进行保护。当采用互斥量保护的资源被占用时,无论是什么优先级的任务,想要使用该资源都会被阻塞

  ②:假如正在使用该资源的任务 1 比阻塞中的任务 2 的优先级低,那么任务 1 的优先级将被系统临时提升到与高优先级任务 2 相等 (任务 1 的优先级从 L 变成 H),这个就是所谓的优先级继承,这样就有效地防止了优先级翻转问题,因为此时优先级介于任务 1 与任务 2 之间的任务,抢占不了 CPU。

  ③:当任务 1 使用完资源之后,释放互斥量,此时任务 1 的优先级会从 H 变回原来的 L

  ④~⑤:任务 2 此时可以获得互斥量,然后进行资源的访问,当任务 2 访问了资源时,该互斥量的状态又变为闭锁状态,其他任务无法获取互斥量。

六、互斥信号量常用的 API 函数

1、使用互斥信号量的典型流程如下:

  1. 创建互斥信号量
  2. 释放互斥信号量
  3. 获取互斥信号量
  4. 删除互斥信号量

2、常用 API 函数如下:

  1. xSemaphoreCreateMutex()
  2. xSemaphoreGive()
  3. xSemaphoreTake()
  4. vSemaphoreDelete()

3、互斥信号量创建与删除

互斥信号量控制块 (句柄)

  如下图:二值信号量的句柄为消息队列的句柄,因为二值信号量是一种长度为 1,消息大小为 0 的特殊消息队列


互斥信号量创建

函数原型:

  SemaphoreHandle_t xSemaphoreCreateMutex(void)

函数描述:

  函数 xSemaphoreCreateMutex 用于创建互斥信号量。

  • 返回值,如果创建成功会返回互斥信号量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不足,无法为此互斥信号量提供所需的空间会返回 NULL。

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

  1. 使用此函数要在 FreeRTOSConfig.h 文件中使能宏定义:#define configUSE_MUTEXES 1

说明:此函数基于消息队列函数实现:

应用举例:

互斥信号量删除

函数原型:

  void vSemaphoreDelete(SemaphoreHandle_t xSemaphore); /* 信号量句柄 */

函数描述:

  函数 vSemaphoreDelete 可用于删除互斥信号量。

4、互斥信号量释放

函数原型:

  xSemaphoreGive(SemaphoreHandle_t xSemaphore); /* 信号量句柄 */

函数描述:

  函数 xSemaphoreGive 用于在任务代码中释放信号量。

  • 第 1 个参数是信号量句柄。
  • 返回值,如果信号量释放成功返回 pdTRUE,否则返回 pdFALSE,因为信号量的实现是基于消息队列,返回失败的主要原因是消息队列已经满了。

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

  1. 此函数是用于任务代码中调用的,不可以在中断服务程序中调用此函数。
  2. 使用此函数前,一定要保证用函数 xSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或者 xSemaphoreCreateCounting() 创建了信号量。
  3. 此函数不支持使用 xSemaphoreCreateRecursiveMutex() 创建的信号量。

应用举例:

5、互斥信号量获取

函数原型:

xSemaphoreTake( SemaphoreHandle_t xSemaphore, /* 信号量句柄 */ 

             TickType_t xTicksToWait ); /* 等待信号量可用的最大等待时间 */

函数描述:

  函数 xSemaphoreTake 用于在任务代码中获取信号量。

  • 第 1 个参数是信号量句柄。
  • 第 2 个参数是没有信号量可用时,等待信号量可用的最大等待时间,单位系统时钟节拍。 返回值,如果创建成功会获取信号量返回 pdTRUE,否则返回 pdFALSE。

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

  1. 此函数是用于任务代码中调用的,不可以在中断服务程序中调用此函数。
  2. 如果消息队列为空且第 2 个参数为 0,那么此函数会立即返回。
  3. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第 2 个参数配置为 portMAX_DELAY,那么此函数会永久等待直到信号量可用。

应用举例:

七、互斥信号量的应用编程

视频讲解,互斥信号量应用于临界资源管理,解决优先级翻转问题
串口输出信息:

STM32cubeMX 配置:



代码:


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值