二进制信号量(binary semaphore)

二进制信号量(binary semaphore)

官方文档参考 6.4 Binary Semaphores Used for Synchronization [[FreeRTOS_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf#page=218&select

官方文档参考

6.4 Binary Semaphores Used for Synchronization

[[FreeRTOS_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf#page=218&selection=5,0,7,42|FreeRTOS_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide, page 218]]

官网文档函数参考

209 4.1 vSemaphoreCreateBinary()

[[FreeRTOS_Reference_Manual_V10.0.0.pdf#page=209&selection=0,3,4,24|FreeRTOS_Reference_Manual_V10.0.0, page 209]]

上面是旧的信号量创建函数,已不再推荐使用,在新版本中,应使用如下函数:

4.2 xSemaphoreCreateBinary()

[[FreeRTOS_Reference_Manual_V10.0.0.pdf#page=212&selection=2,0,4,24|FreeRTOS_Reference_Manual_V10.0.0, page 212]]


什么是信号量

信号量

如同红绿灯一样,控制任务(Task)进行同步,哪些任务可以优先执行,哪些任务可以暂停。

二进制信号量

二进制信号量只有两个值,要么0要么1,1 的时候就开始执行,0 的时候停止执行,经常使用信号量的方式进行同步。

xSemaphoreCreateBinary():

#include "FreeRTOS.h"
#include "semphr.h"

SemaphoreHandle_t xSemaphoreCreateBinary( void );

函数说明:

  • 创建一个二进制信号量,返回一个可以引用的信号量句柄。
  • RAM needed.
  • 信号量创建初始状态为空,意味着要先给它一个信号(Give),信号量才能正常使用。

参数:

  • None:无参数。

返回值:

  • NULL: FreeRTOS 的堆中没有足够的内存,无法创建信号量句柄。
  • Any other value:信号量创建成功,返回一个可以引用的信号量句柄。

注意:

  • 直接给任务下发通知,通常要比信号量更轻便、更快速。
  • 信号量和互斥锁非常相似,但是一些微小的区别。互斥锁包含着优先级继承机制,而二进制信号量没有。这个导致二进制信号量是处理任务同步时的最佳选择。互斥锁是实现简单互斥的更好选择。 ^46986d
    • 二进制信号量:
      • 用于同步的二进制信号量在成功“获取”后不需要再“归还”。任务同步是通过一个中断或任务“给出“的信号量来实现的,同时别的任务“获取”信号量。请注意,使用直接任务通知,通常可以逢有效地实现任务同步。
    • 互斥锁: ^0e1b84
      • 如果另一个优先级更高的任务尝试获取互斥锁,则持有互斥锁的任务的优先级会提高。
      • 已经获取互斥锁的任务,会“继承”试图获取相同互斥锁的任务的优先级。

      也就是说,我持有,但是你的地位高,并且你要,因而我的地位会和你一样高,让你获取不到互斥锁。

      • 互斥锁返回后,任务继承的优先级会被还原。
      • 任务在使用完互斥锁后,必须始终归还互斥锁。换句话说,任何任务不可能一直持有同一个互斥锁。
  • 互斥锁和二进制信号量均使用SemaphoreHandle_t,并且可以在任何用此类型形参的 API 函数中使用。
  • 互斥锁和二进制信号量都可以使用函数xSemaphoreCreateBinary()创建的信号量。

xSemaphoreTake()

#include "FreeRTOS.h"
#include "semphr.h"

BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

函数说明:

  • 获取之前用xSemaphoreCreate(), xSemaphoreCreateCounting(), xSemaphoreCreateMutex()创建的信号量。

参数:

  • xSemaphore:用于获取信号量的句柄。
  • xTicksToWait:超时等待时间。

返回值:

  • pdPASS:成功获取到信号量。
  • pdFAIL:获取信号量失败。

注意事项:

  • 必须在运行中的任务调用此函数。
  • 不得在程序关键部分或任务暂停( Suspend )时调用此函数。

xSemaphoreGive()

#include "FreeRTOS.h"
#include "semphr.h"

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );

函数说明:

  • 创建一个信号量之后,首先要释放这个信号量。
  • vSemaphoreCreateBinary(), xSemaphoreCreateCounting(), xSemaphoreCreateMutex(),用这些函数创建信号量之后,均应使用此函数来先释放信号量。

参数:

  • xSemaphore:需要被释放的信号量句柄。

返回值:

  • pdPASS:释放信号量成功。
  • pdFAIL:信号量释放失败,因为调用此函数的任务并非信号量的持有者。任何任务必须成功“获取”信号量,然后才能“归还”信号量。

需要在创建信号量之后(xSemaphoreCreateBinary()...),用此函数(xSemaphoreGive())首先给一个信号量,才能正常锁住数据。

示例代码

此处代码信号量的作用是“锁住”数据,即 一个任务对数据进行修改时,另一个任务不可对相同的数据修改。

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"

static SemaphoreHandle_t semaphore_handle; // 创建信号量句柄,公共使用
static int count = 0; // 即将上锁的数据

void task_1(void *pvPara)
{
    while (true) {
		// 开启(拿走)信号,即表明,“我”要对数据进行更改了,另一个任务不可同时对公共数据修改
        xSemaphoreTake(semaphore_handle, portMAX_DELAY);

        for (int i = 0; i < 10; i++) {
            count++;
            printf("[TASK 1]count = %d\n", count);
            vTaskDelay(pdMS_TO_TICKS(1000));
        }

		// 把信号还回去,即 其他任务可以对公共数据修改了
        xSemaphoreGive(semaphore_handle);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void task_2(void *pvPara)
{
    while (true) {
        xSemaphoreTake(semaphore_handle, portMAX_DELAY);

        for (int i = 0; i < 10; i++) {
            count++;
            printf("[TASK 2]count = %d\n", count);
            vTaskDelay(pdMS_TO_TICKS(1000));
        }

        xSemaphoreGive(semaphore_handle);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void app_main(void)
{
	// 创建信号量
    semaphore_handle = xSemaphoreCreateBinary();
	// 信号量创建完成后,首先要给予一个信号
    xSemaphoreGive(semaphore_handle);

    xTaskCreate(task_1, "Task 1", 2048, (void *)semaphore_handle, 1, NULL);
    xTaskCreate(task_2, "Task 2", 2048, (void *)semaphore_handle, 1, NULL);
}

示例输出

15. 二进制信号量(binary semaphore)-20240615224504305.webp

程序输出表明,两个任务分别对公共数据进行连续读写,在一个任务读写数据时,另一个任务完全不干扰。如果不用信号量对数据上锁,则两个任务会交替续写公共数据。

相当于信号灯,Task 1 绿灯时,有权读写数据,而 Task 2 无权读写;Task 2 绿灯时,有权读写数据, 而 Task 1 无权读写数据。

Comment