本文记录我初步学习FreeRTOS的信号量的知识,在此记录分享,希望我的分享对你有所帮助!
什么是信号量
在FreeRTOS中,信号量(Semaphore)是一种用于任务间同步和资源共享的机制。信号量主要用于管理对共享资源的访问,以防止并发访问导致的竞态条件和数据损坏。
具体来说,信号量有两种类型:
二进制信号量(Binary Semaphore):
- 二进制信号量只能取两个值:0和1。
- 它通常用于实现互斥访问共享资源,比如一个任务使用完某个资源后释放信号量,另一个任务才能获得该资源。
计数信号量(Counting Semaphore):
- 计数信号量可以取多个值,通常是一个正整数。
- 它用于管理一组相同类型的资源,例如有限数量的内存块或者可以同时处理的任务数目。
FreeRTOS中信号量的基本操作包括:
xSemaphoreCreateBinary()
:创建一个二进制信号量。xSemaphoreCreateCounting()
:创建一个计数信号量。xSemaphoreTake()
:获取(等待)一个信号量。xSemaphoreGive()
:释放一个信号量。使用信号量时,任务可以通过获取和释放信号量来保护共享资源或者控制任务的执行顺序,从而有效地管理并发访问。
信号量常常用于控制对共享资源的访问和任务同步。举一个很常见的例子,某个停车场有100个停车位,这100个停车位大家都可以用,对于大家来说这100个停车位就是共享资源。假设现在这个停车场正常运行,你要把车停到这个这个停车场肯定要先看一下现在停了多少车了?还有没有停车位?当前停车数量就是一个信号量,具体的停车数量就是这个信号量值,当这个值到100的时候说明停车场满了。停车场满的时你可以等一会看看有没有其他的车开出停车场,当有车开出停车场的时候停车数量就会减一,也就是说信号量减一,此时你就可以把车停进去了,你把车停进去以后停车数量就会加一,也就是信号量加一。这就是一个典型的使用信号量进行共享资源管理的案例,在这个案例中使用的就是计数型信号量。
再看另外一个案例:使用公共电话,我们知道一次只能一个人使用电话,这个时候公共电话就只可能有两个状态:使用(1)或未使用(0),如果用电话的这两个状态作为信号量的话,那么这个就是二值信号量。
信号量用于控制共享资源访问的场景相当于一个上锁机制,代码只有获得了这个锁的钥匙才能够执行。
上面我们讲了信号量在共享资源访问中的使用,信号量的另一个重要的应用场合就是任务同步,用于任务与任务或中断与任务之间的同步。在执行中断服务函数的时候可以通过向任务发送信号量来通知任务它所期待的事件发生了,当退出中断服务函数以后在任务调度器的调度下同步的任务就会执行。
在编写中断服务函数的时候我们都知道一定要快进快出,中断服务函数里面不能放太多的代码,否则的话会影响中断的实时性。裸机编写中断服务函数的时候一般都只是在中断服务函数中打个标记,然后在其他的地方根据标记来做具体的处理过程。
在使用RTOS系统的时候我们就可以借助信号量完成此功能,当中断发生的时候就释放信号量,中断服务函数不做具体的处理。具体的处理过程做成一个任务,这个任务会获取信号量,如果获取到信号量就说明中断发生了,那么就开始完成相应的处理,这样做的好处就是中断执行时间非常短。这个例子就是中断与任务之间使用信号量来完成同步,当然了,任务与任务之间也可以使用信号量来完成同步。
下面举一个例子来说明FreeRTOS中信号量的使用:
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"// 定义一个全局变量作为共享资源
volatile int shared_resource = 0;// 声明一个二进制信号量句柄
SemaphoreHandle_t mutex;void Task1(void *pvParameters) {while (1) {// 等待获取信号量,相当于获得了锁xSemaphoreTake(mutex, portMAX_DELAY);// 访问共享资源shared_resource++;printf("Task1: Incremented shared_resource to %d\n", shared_resource);// 释放信号量,相当于释放了锁xSemaphoreGive(mutex);vTaskDelay(pdMS_TO_TICKS(1000)); // 模拟任务执行延迟}
}void Task2(void *pvParameters) {while (1) {// 等待获取信号量,相当于获得了锁xSemaphoreTake(mutex, portMAX_DELAY);// 访问共享资源shared_resource--;printf("Task2: Decremented shared_resource to %d\n", shared_resource);// 释放信号量,相当于释放了锁xSemaphoreGive(mutex);vTaskDelay(pdMS_TO_TICKS(1500)); // 模拟任务执行延迟}
}int main(void) {// 创建二进制信号量mutex = xSemaphoreCreateBinary();// 创建任务 Task1xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);// 创建任务 Task2xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);// 启动调度器vTaskStartScheduler();// 不会运行到这里return 0;
}
- 我们定义了一个全局变量
shared_resource
作为共享资源。- 我们声明了一个二进制信号量
mutex
,用于保护shared_resource
的访问。- Task1 和 Task2 分别是两个任务,它们会竞争访问
shared_resource
。- 在每个任务中,通过
xSemaphoreTake(mutex, portMAX_DELAY)
获取信号量(相当于获得锁),访问shared_resource
后再通过xSemaphoreGive(mutex)
释放信号量(相当于释放锁)。这样,通过信号量的使用,我们确保了在任意时刻只有一个任务可以访问
shared_resource
,避免了数据竞争和不确定的行为。