• FreeRTOS学习笔记-信号量


    概述

    Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于N=1的情况,称为binary semaphore,一般用于程序同步,或限制某一资源的同时访问。

    – 参考 https://blog.csdn.net/m0_37817135/article/details/109578289

    二值信号量一般用于程序同步,互斥锁则一般用于资源保护。

    关键函数说明

    //创建二值信号量
    SemaphoreHandle_t xSemaphoreCreateBinary( void );
    //返回值:NULL == 创建失败;other value: 信号量句柄
    
    //创建互斥锁
    SemaphoreHandle_t xSemaphoreCreateMutex( void );
    //互斥锁创建后无需Give。
    
    //创建递归互持锁
    SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );
    
    //创建计数信号量
    SemaphoreHandle_t xSemaphoreCreateCounting( 
    			UBaseType_t uxMaxCount,//最大空位
    			UBaseType_t uxInitialCount //初始空位
    			);
    
    //删除信号量
    void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
    
    //获取信号量
    BaseType_t xSemaphoreTake( 
    			SemaphoreHandle_t xSemaphore, //信号量句柄
    			TickType_t xTicksToWait //如果信号量被占用所需要等待的时间,0==立即返回,portMAX_DELAY == 死等
    			);
    //返回值:pdPASS == 成功;pdFAIL == 失败
    
    //获取递归互斥锁
    BaseType_t xSemaphoreTakeRecursive( 
    			SemaphoreHandle_t xSemaphore, //信号量句柄
    			TickType_t xTicksToWait //如果信号量被占用所需要等待的时间,0==立即返回,portMAX_DELAY == 死等
    			);
    //返回值:pdPASS == 成功;pdFAIL == 失败
    
    //释放信号量
    BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
    //返回值:pdPASS == 成功释放;pdFAIL== 失败
    
    //释放递归互斥锁
    BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore );
    //返回值:pdPASS == 成功释放;pdFAIL== 失败
    
    //获取信号量空位
    UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    课程示例

    二值信号量的使用

    #include 
    #include "sdkconfig.h"
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "esp_system.h"
    #include "esp_spi_flash.h"
    #include "freertos/semphr.h"
    
    SemaphoreHandle_t Semaphore_iCount;
    int iCount = 100;
    
    void mytask1(void *pvParameter)
    {
        xSemaphoreTake(Semaphore_iCount, portMAX_DELAY);
        for (int i = 0; i < 5; i++)
        {
            printf("task1: iCount = %d\n", iCount);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            iCount++;
        }
        xSemaphoreGive(Semaphore_iCount);
        vTaskDelete(NULL);
    }
    void mytask2(void *pvParameter)
    {
        xSemaphoreTake(Semaphore_iCount, portMAX_DELAY);
        for (int i = 0; i < 5; i++)
        {
            printf("task2: iCount = %d\n", iCount);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            iCount++;
        }
        xSemaphoreGive(Semaphore_iCount);
        vTaskDelete(NULL);
    }
    void app_main(void)
    {
        Semaphore_iCount = xSemaphoreCreateBinary();
        xSemaphoreGive(Semaphore_iCount);
        printf("app_main: Create and Give Semaphore_iCount \n");
    
        TaskHandle_t myTastHandle1 = NULL;
        TaskHandle_t myTastHandle2 = NULL;
        xTaskCreate(mytask1, "mytask1", 1024 * 5, NULL, 1, &myTastHandle1);
        xTaskCreate(mytask2, "mytask2", 1024 * 5, NULL, 1, &myTastHandle2);
        printf("app_main: Create task1 and task2 \n");
    
        // 10s后删除任务和信号量,退出app_main
        vTaskDelay(10000 / portTICK_PERIOD_MS);
        vSemaphoreDelete(Semaphore_iCount);
        printf("app_main: Delete task1 、 task2 and Semaphore_iCount , exit app_main \n");
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    效果

    app_main: Create and Give Semaphore_iCount
    app_main: Create task1 and task2
    task1: iCount = 100
    task1: iCount = 101
    task1: iCount = 102
    task1: iCount = 103
    task1: iCount = 104
    task2: iCount = 105
    task2: iCount = 106
    task2: iCount = 107
    task2: iCount = 108
    task2: iCount = 109
    app_main: Delete task1 、 task2 and Semaphore_iCount , exit app_main

    使用一个二值信号量对iCount进行保护,任务1和任务2各处理5s,屏蔽信号量效果如下:

    app_main: Create and Give Semaphore_iCount
    app_main: Create task1 and task2
    task1: iCount = 100
    task2: iCount = 100
    task1: iCount = 101
    task2: iCount = 102
    task1: iCount = 103
    task2: iCount = 104
    task1: iCount = 105
    task2: iCount = 106
    task1: iCount = 107
    task2: iCount = 108
    app_main: Delete task1 、 task2 and Semaphore_iCount , exit app_main

    计数信号量使用

    #include 
    #include "sdkconfig.h"
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "esp_system.h"
    #include "esp_spi_flash.h"
    #include "freertos/semphr.h"
    
    SemaphoreHandle_t Semaphore_parking_space;
    
    void car_in_task(void *pvParameter)
    {
        while (1)
        {
            printf("parking_space = %d\n", uxSemaphoreGetCount(Semaphore_parking_space));
            BaseType_t r = xSemaphoreTake(Semaphore_parking_space, 0);
            if (r == pdPASS)
            {
                printf("One car in \n");
            }
            else
            {
                printf("No space \n");
            }
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
    }
    void car_out_task(void *pvParameter)
    {
        while (1)
        {
            vTaskDelay(6000 / portTICK_PERIOD_MS);
            xSemaphoreGive(Semaphore_parking_space);
            printf("One car out \n");
        }
    }
    void app_main(void)
    {
        Semaphore_parking_space = xSemaphoreCreateCounting(5, 5);
        xTaskCreate(car_in_task, "car_in_task", 1024 * 5, NULL, 1, NULL);
        xTaskCreate(car_out_task, "car_out_task", 1024 * 5, NULL, 1, NULL);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    效果

    parking_space = 5
    One car in
    parking_space = 4
    One car in
    parking_space = 3
    One car in
    parking_space = 2
    One car in
    parking_space = 1
    One car in
    parking_space = 0
    No space
    One car out
    parking_space = 1
    One car in
    parking_space = 0
    No space
    parking_space = 0
    No space

    互斥锁的使用

    #include 
    #include "sdkconfig.h"
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "esp_system.h"
    #include "esp_spi_flash.h"
    #include "freertos/semphr.h"
    
    SemaphoreHandle_t Semaphore_iCount;
    int iCount = 0;
    
    void mytask1(void *pvParameter)
    {
        BaseType_t iRet;
        while (1)
        {
            iRet = xSemaphoreTake(Semaphore_iCount, 0);
            if (iRet == pdPASS)
            {
                for (int i = 0; i < 5; i++)
                {
                    printf("task1: iCount = %d\n", iCount);
                    vTaskDelay(1000 / portTICK_PERIOD_MS);
                    iCount++;
                }
                xSemaphoreGive(Semaphore_iCount);
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
            else
            {
                printf("task1: didn't take \n");
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
        }
    }
    void mytask2(void *pvParameter)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        while (1)
            ;
    }
    void mytask3(void *pvParameter)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        BaseType_t iRet;
        while (1)
        {
            iRet = xSemaphoreTake(Semaphore_iCount, 1);//注意参数等待时间,必须大于0,否则任务1获取互斥锁也无法继承任务3的优先级。
            if (iRet == pdPASS)
            {
                for (int i = 0; i < 5; i++)
                {
                    printf("task3: iCount = %d\n", iCount);
                    vTaskDelay(1000 / portTICK_PERIOD_MS);
                    iCount++;
                }
                xSemaphoreGive(Semaphore_iCount);
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
            else
            {
                printf("task3: didn't take \n");
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
        }
    }
    void app_main(void)
    {
        //Semaphore_iCount = xSemaphoreCreateBinary();
        Semaphore_iCount = xSemaphoreCreateMutex();//互斥锁
    
        //xSemaphoreGive(Semaphore_iCount);
    
        vTaskSuspendAll();
        xTaskCreatePinnedToCore(mytask1, "mytask1", 1024 * 5, NULL, 1, NULL, 0); //最后一个参数是Core ID
        xTaskCreatePinnedToCore(mytask2, "mytask2", 1024 * 5, NULL, 2, NULL, 0);
        xTaskCreatePinnedToCore(mytask3, "mytask3", 1024 * 5, NULL, 3, NULL, 0);
        xTaskResumeAll();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    效果

    task1: iCount = 0
    task1: iCount = 1
    task3: didn’t take
    task1: iCount = 2
    task3: didn’t take
    task1: iCount = 3
    task3: didn’t take
    task1: iCount = 4
    task3: didn’t take
    task3: iCount = 5
    E (12285) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
    E (12285) task_wdt: - IDLE (CPU 0)
    E (12285) task_wdt: Tasks currently running:
    E (12285) task_wdt: CPU 0: mytask2
    E (12285) task_wdt: CPU 1: IDLE
    E (12285) task_wdt: Print CPU 0 (current core) backtrace
    task3: iCount = 6
    task3: iCount = 7
    task3: iCount = 8
    task3: iCount = 9
    E (17285) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
    E (17285) task_wdt: - IDLE (CPU 0)
    E (17285) task_wdt: Tasks currently running:
    E (17285) task_wdt: CPU 0: mytask2
    E (17285) task_wdt: CPU 1: IDLE
    E (17285) task_wdt: Print CPU 0 (current core) backtrace
    task3: iCount = 10
    task3: iCount = 11
    task3: iCount = 12
    task3: iCount = 13
    task3: iCount = 14
    E (22285) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
    E (22285) task_wdt: - IDLE (CPU 0)
    E (22285) task_wdt: Tasks currently running:
    E (22285) task_wdt: CPU 0: mytask2
    E (22285) task_wdt: CPU 1: IDLE
    E (22285) task_wdt: Print CPU 0 (current core) backtrace

    任务1在获取信号量的同时继承了任务3的优先级,临时提高自身优先级,等任务1释放信号量以后,优先级降低,任务2死循环,任务1再不会被调度.
    任务2死循环不但让任务1,还有加入看门狗的IDLE任务也无法执行,因此每隔5s会打印任务看门狗异常。
    实际测试发现互斥锁,在take的时候等待时间为0则任务1无法继承任务3的优先级,待查。

    递归锁的使用

    递归锁用于保护一次任务中需要级联调用的多个资源,如下图,任务1中先获取A资源,在A中又使用B资源,最后需要先释放B、再释放A。
    在这里插入图片描述

    #include 
    #include "sdkconfig.h"
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "esp_system.h"
    #include "esp_spi_flash.h"
    #include "freertos/semphr.h"
    
    SemaphoreHandle_t Semaphore_iCount;
    
    void mytask1(void *pvParameter)
    {
        while (1)
        {
            xSemaphoreTakeRecursive(Semaphore_iCount, 0);
            for (int i = 0; i < 3; i++)
            {
                printf("-----------\n");
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
            xSemaphoreTakeRecursive(Semaphore_iCount, 0);
            for (int i = 0; i < 5; i++)
            {
    
                printf("------------------------\n");
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
            xSemaphoreGiveRecursive(Semaphore_iCount);
            for (int i = 0; i < 3; i++)
            {
                printf("-----------\n");
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
            xSemaphoreGiveRecursive(Semaphore_iCount);
            vTaskDelay(3000 / portTICK_PERIOD_MS);
        }
    }
    
    void mytask2(void *pvParameter)
    {
        BaseType_t iRet;
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        while (1)
        {
            iRet = xSemaphoreTakeRecursive(Semaphore_iCount, 0);
            if (iRet == pdPASS)
            {
                xSemaphoreGiveRecursive(Semaphore_iCount);
                printf(">>>>>>>>>>>>>>>>>>>>>\n");
            }
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
    }
    
    void app_main(void)
    {
    
        Semaphore_iCount = xSemaphoreCreateRecursiveMutex(); //创建递归互斥锁
    
        xTaskCreatePinnedToCore(mytask1, "mytask1", 1024 * 5, NULL, 1, NULL, 0); //最后一个参数是Core ID
        xTaskCreatePinnedToCore(mytask2, "mytask2", 1024 * 5, NULL, 2, NULL, 0);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    效果

    -----------
    -----------
    -----------
    ------------------------
    ------------------------
    ------------------------
    ------------------------
    ------------------------
    -----------
    -----------
    -----------
    >>>>>>>>>>>>>>>>>>>>>
    >>>>>>>>>>>>>>>>>>>>>
    >>>>>>>>>>>>>>>>>>>>>
    -----------
    -----------
    -----------
    ------------------------
    ------------------------
    ------------------------
    ------------------------
    ------------------------
    -----------
    -----------
    -----------
    >>>>>>>>>>>>>>>>>>>>>
    >>>>>>>>>>>>>>>>>>>>>
    >>>>>>>>>>>>>>>>>>>>>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    任务1在2级递归调用互斥锁没有全部释放的时候,任务2无法获取互斥锁。

  • 相关阅读:
    【自用笔记】nginx 配置多 context 及简单负载均衡
    ES _bulk 批量操作用法
    【疑问解决】- 源码Enmu枚举类的toString里面的name是哪里来的,什么时候传入的?
    Java-IO流之字节输入流(中篇)
    无人机航拍技术基础入门,无人机拍摄的方法与技巧
    typescript80-使用配置文件编译文件
    init container
    C++类的运算符重载.md
    架构师习题--嵌入式习题
    安卓逆向 - EdXposed LSPosed VirtualXposed
  • 原文地址:https://blog.csdn.net/ai_ljh/article/details/126797192