一、信号量
信号量是任务间同步的一种机制,信号量可以用在多任务访问同一资源时的资源管理。FreeRTOS 提供了多种信号量,按信号量的功能可分为二值信号量、计数型信号量、互斥信号量和递归互斥信号量,不同类型的信号量有其不同的应用场景,合理地使用信号量可以帮助开发者快速开发稳健的系统。
信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。其中,“同步”指的是任务间的同步,即信号量可以使得一个任务等待另一个任务完成某件事情后,才继续执行;而“有序访问”指的是对被多任务或中断访问的共享资源的管理,当一个任务在访问一个共享资源时,信号量可以防止其他任务或中断在这期间访问这个共享资源。
二、二值信号量
信号量是基于队列实现的,二值信号量也不例外,二值信号量实际上就是一个队列长度为 1 的队列,在这种情况下,队列就只有空和满两种情况,这不就是二值情况吗?二值信号量通常用于互斥访问或任务同步,与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题。优先级翻转问题指的是,当一个高优先级任务因获取一个被低优先级任务获取而处于没有资源状态的二值信号量时,这个高优先级的任务将被阻塞,直到低优先级的任务释放二值信号量,而在这之前,如果有一个优先级介于这个高优先级任务和低优先级任务之间的任务就绪,那么这个中等优先级的任务就会抢占低优先级任务的运行,这么一来,这三个任务中优先级最高的任务反而要最后才行。这就是二值信号量带来的优先级翻转问题,用户在实际开发中要注意这种问题。
和队列一样,在获取二值信号量的时候,允许设置一个阻塞超时时间,阻塞超时时间是当任务获取二值信号量时,由于二值信号量处于没有资源的状态,而导致任务进入阻塞状态的最大系统时钟节拍数。如果多个任务同时因获取同一个处于没有资源状态的二值信号量而被阻塞,那么在二值信号量有资源的时候,这些阻塞任务中优先级高的任务将优先获得二值信号量的资源并解除阻塞
三、计数型信号量
计数型信号量与二值信号量类似,二值信号量相当于队列长度为 1 的队列,因此二值信号量只能容纳一个资源,这也是为什么命名为二值信号量,而计数型信号量相当于队列长度大于0 的队列,因此计数型信号量能够容纳多个资源,这是在计数型信号量被创建的时候确定的。
计数型信号量通常用于一下两种场合:
1. 事件计数
在这种场合下,每次事件发生后,在事件处理函数中释放计数型信号量(计数型信号量的
资源数加 1),其他等待事件发生的任务获取计数型信号量(计数型信号量的资源数减 1),这么
一来等待事件发生的任务就可以在成功获取到计数型信号量之后执行相应的操作。在这种场合
下,计数型信号量的资源数一般在创建时设置为 0。
2. 资源管理
在这种场合下,计数型信号量的资源数代表着共享资源的可用数量。一个任务想要访问共享资源,就必须先获取这个共享资源的计数型信号量,之后在成功获取了计数型信号量之后,才可以对这个共享资源进行访问操作,当然,在使用完共享资源后,也要释放这个共享资源的计数型信号量。在这种场合下,计数型信号量的资源数一般在创建时设置为受其管理的共享资源的最大可用数量。
四、互斥信号量
互斥信号量其实就是一个拥有优先级继承的二值信号量。在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一把钥匙,当任务想要访问共享资源的时候就必须先获得这把钥匙,当访问完共享资源以后就必须归还这把钥匙,这样其他的任务就可以拿着这把钥匙去访问资源。互斥信号量使用和二值信号量相同的API 操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的机制。优先级继承是:当一个互斥信号量正在被一个低优先级的任务持有时,如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。优先级继承尽可能的减少了高优先级任务处于阻塞态的时间,并且将“优先级翻转”的影响降到最低。
优先级继承并不能完全的消除优先级翻转的问题,优先级继承只是尽可能的降低优先级翻转带来的影响。实时应用应该在设计之初就要避免优先级翻转的发生。
互斥信号量不能用于中断服务函数中,原因如下:
(1) 互斥信号量有任务优先级继承的机制,但是中断不是任务,没有任务优先级,所以互斥信号量只能用与任务中,不能用于中断服务函数。
(2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。
五、递归互斥信号量
可以看作是特殊的互斥信号量,与互斥信号量不同的是,递归互斥信号量在被获取后,可以被其持有者重复获取,但是递归互斥信号量的持有者需要释放递归互斥信号量 时,需要释放 获取递归互斥信号量的次数 ,这样递归互斥信号量才算被释放。递归互斥信号量与互斥信号量一样,也具备优先级继承机制,因此也不能在中断服务函数中使用递归互斥信号量。
六、互斥信号量和二值信号量的区别
互斥型信号量:同一个任务申请,同一个任务释放,其他任务释放无效。同一个任务可以递归申请。互斥信号量也不同于二值信号量的是互斥信号量具有优先级继承的机制。
二值信号量:一个任务申请成功后,可以由另一个任务释放。
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。互斥保证的是一个资源同时只能由一个人拥有,只有他不要了,其他人才能拥有。
同步:是指在互斥的基础上,通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥。
七、二值信号量实验
//任务1释放信号量,任务2获取信号量
#include "freeRTOS_demo.h"
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );
QueueHandle_t semphore_handle;
void freertos_demo(void)
{
semphore_handle = xSemaphoreCreateBinary();
if(semphore_handle != NULL)
{
vTaskSuspendAll();
printf("xSemaphoreCreateBinary success");
xTaskResumeAll(); //恢复OS调度
}
xTaskCreate((TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t * ) &start_task_handler );
vTaskStartScheduler();
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
xTaskCreate((TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
xTaskCreate((TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
vTaskDelete(NULL);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
void task1( void * pvParameters )
{
BaseType_t err;
if(semphore_handle != NULL)
{
err = xSemaphoreGive(semphore_handle);
if(err == pdPASS)
{
printf("xSemaphoreGive success\r\n");
}
else
printf("xSemaphoreGive fail\r\n");
}
while(1)
{
}
}
void task2( void * pvParameters )
{
uint32_t i = 0;
BaseType_t err;
while(1)
{
err = xSemaphoreTake(semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
if(err == pdTRUE)
{
printf("xSemaphoreTake success\r\n");
}
else
{
printf("timeout %d\r\n",++i);
}
}
}
八、计数型信号量实验
//任务1每0.5秒释放信号量,任务2每1秒获取信号量,实验结果是每一秒信号量都会加1
#include "FreeRTOS_demo.h"
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );
#define TASK1_PRIO 3
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );
#define TASK2_PRIO 2
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );
QueueHandle_t count_semphore_handle;
void freertos_demo(void)
{
count_semphore_handle = xSemaphoreCreateCounting(100 , 0);
if(count_semphore_handle != NULL)
{
vTaskSuspendAll();
printf("计数型信号量创建成功!!!\r\n");
xTaskResumeAll();
}
xTaskCreate((TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t * ) &start_task_handler );
vTaskStartScheduler();
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
xTaskCreate((TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
xTaskCreate((TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
vTaskDelete(NULL);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
void task1( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
if(count_semphore_handle != NULL)
{
xSemaphoreGive(count_semphore_handle); /* 释放信号量 */
}
vTaskDelay(500);
}
}
void task2( void * pvParameters )
{
BaseType_t err = 0;
while(1)
{
err = xSemaphoreTake(count_semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
if(err == pdTRUE)
{
printf("信号量的计数值为:%d\r\n",(int)uxSemaphoreGetCount(count_semphore_handle));
}
vTaskDelay(1000);
}
}
九、互斥信号量实验
//互斥信号量就是资源只能被同一个任务获取和释放,低优先级任务和高优先级任务都会申请和释放互斥量,但是只有某一个任务释放之后另一个任务才会申请成功
#include "FreeRTOS_demo.h"
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t low_task_handler;
void low_task( void * pvParameters );
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t middle_task_handler;
void middle_task( void * pvParameters );
#define TASK3_PRIO 4
#define TASK3_STACK_SIZE 128
TaskHandle_t high_task_handler;
void high_task( void * pvParameters );
QueueHandle_t mutex_semphore_handle;
void freertos_demo(void)
{
mutex_semphore_handle = xSemaphoreCreateMutex(); /* 创建互斥信号量,并且主动释放一次信号量 */
xTaskCreate((TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t * ) &start_task_handler );
vTaskStartScheduler();
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL();
xTaskCreate((TaskFunction_t ) low_task,
(char * ) "low_task",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &low_task_handler );
xTaskCreate((TaskFunction_t ) middle_task,
(char * ) "middle_task",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &middle_task_handler );
xTaskCreate((TaskFunction_t ) high_task,
(char * ) "high_task",
(configSTACK_DEPTH_TYPE ) TASK3_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK3_PRIO,
(TaskHandle_t * ) &high_task_handler );
vTaskDelete(NULL);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* 任务一,低优先级任务 */
void low_task( void * pvParameters )
{
while(1)
{
printf("low_task阻塞\r\n");
xSemaphoreTake(mutex_semphore_handle,portMAX_DELAY);
printf("low_task获得\r\n");
vTaskDelay(3000);
printf("low_task释放\r\n");
xSemaphoreGive(mutex_semphore_handle);
vTaskDelay(1000);
}
}
/* 任务二,中优先级任务 */
void middle_task( void * pvParameters )
{
while(1)
{
printf("middle_task正在运行!!!\r\n");
vTaskDelay(1000);
}
}
/* 任务三,高优先级任务 */
void high_task( void * pvParameters )
{
while(1)
{
printf("high_task阻塞\r\n");
xSemaphoreTake(mutex_semphore_handle,portMAX_DELAY);
printf("high_task获取\r\n");
vTaskDelay(1000);
printf("high_task释放\r\n");
xSemaphoreGive(mutex_semphore_handle);
vTaskDelay(1000);
}
}