概述
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 );
课程示例
二值信号量的使用
#include <stdio.h>
#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");
}
效果
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 <stdio.h>
#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);
}
效果
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 <stdio.h>
#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();
}
效果
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 <stdio.h>
#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级递归调用互斥锁没有全部释放的时候,任务2无法获取互斥锁。