下面我将通过以下几个方面,来详细讲解“FreeRTOS实时操作系统信号量基础”的完整攻略:
- 信号量是什么
- FreeRTOS的信号量及其基础API
- 信号量的使用示例说明
- 进一步扩展: 二值信号量和计数信号量
1. 信号量是什么
信号量是一种基本的同步机制,在多任务并发执行、共享资源时起到重要作用。它可以控制多个任务对共享资源的访问顺序,保证每个任务能够按照一定的顺序获取共享资源而不会冲突,从而保证系统的正确运行。
2. FreeRTOS的信号量及其基础API
FreeRTOS提供了两种类型的信号量:二值信号量和计数信号量。
二值信号量的取值范围只有两个:0和1。该类型的信号量通常用于同步任务之间的行为,比如某个任务执行完了一个关键操作后通知其他任务开始执行某个操作。
计数信号量的取值范围可以是任意正整数。该类型的信号量通常用于控制资源访问的数量,比如限制同时只有两个任务可以对某个资源进行访问。
以下是FreeRTOS提供的信号量API:
xSemaphoreCreateBinary()
:创建一个二值信号量xSemaphoreCreateCounting(uxMaxCount, uxInitialCount)
:创建一个计数信号量,uxMaxCount表示允许的最大值,uxInitialCount表示初始值xSemaphoreTake(xSemaphore, xBlockTime)
:指定一个信号量,该函数会尝试获取该信号量。如果信号量的取值为0,取值操作将会阻塞(如果指定了阻塞时间xBlockTime则会阻塞一段时间),直到信号量的取值为1或其他任务释放该信号量xSemaphoreGive(xSemaphore)
:将一个信号量的值加1,相当于释放该信号量xSemaphoreGiveFromISR(xSemaphore, pxHigherPriorityTaskWoken)
:和xSemaphoreGive的差不多,只是该函数可以在中断里面使用。如果该函数使得某个任务的优先级变得比当前任务高,那么参数pxHigherPriorityTaskWoken将被设置为非零值
3. 信号量的使用示例说明
我们先看一个简单的二值信号量的使用示例:
#include "FreeRTOS.h"
#include "semphr.h"
SemaphoreHandle_t s_binary;
void task_A(void* params) {
while (1) {
// 尝试获取二值信号量
if (xSemaphoreTake(s_binary, portMAX_DELAY) == pdTRUE) {
// 用打印语句替代具体的任务处理
printf("Task A Acquire Binary Semaphore.\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
// 释放二值信号量
xSemaphoreGive(s_binary);
}
}
}
void task_B(void* params) {
while (1) {
// 尝试获取二值信号量
if (xSemaphoreTake(s_binary, portMAX_DELAY) == pdTRUE) {
// 用打印语句替代具体的任务处理
printf("Task B Acquire Binary Semaphore.\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
// 释放二值信号量
xSemaphoreGive(s_binary);
}
}
}
int main() {
// 创建一个二值信号量
s_binary = xSemaphoreCreateBinary();
xTaskCreate(task_A, "Task A", 512, NULL, 1, NULL);
xTaskCreate(task_B, "Task B", 512, NULL, 2, NULL);
// 启动调度器
vTaskStartScheduler();
return 0;
}
在这个示例中,我们创建了一个二值信号量s_binary
,两个任务(task_A
和task_B
)将会通过该信号量进行同步。每个任务的执行方式类似,大致的流程如下:
- 尝试获取二值信号量
s_binary
; - 如果获取成功,说明该任务可以开始执行,用打印语句替代具体的任务处理,等待1秒;
- 释放二值信号量
s_binary
;
从示例中我们可以看出,只要二值信号量被某个任务获取,其他任务就无法获取该信号量,直到该任务将其释放。
我们再来看一个计数信号量的使用示例:
#include "FreeRTOS.h"
#include "semphr.h"
SemaphoreHandle_t s_counting;
void task_C(void* params) {
while (1) {
// 尝试获取计数信号量
if (xSemaphoreTake(s_counting, portMAX_DELAY) == pdTRUE) {
// 用打印语句替代具体的任务处理
printf("Task C Acquire Counting Semaphore.\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
// 释放计数信号量
xSemaphoreGive(s_counting);
}
}
}
void task_D(void* params) {
while (1) {
// 尝试获取计数信号量
if (xSemaphoreTake(s_counting, portMAX_DELAY) == pdTRUE) {
// 用打印语句替代具体的任务处理
printf("Task D Acquire Counting Semaphore.\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
// 释放计数信号量
xSemaphoreGive(s_counting);
}
}
}
int main() {
// 创建一个计数信号量,允许的最大值为3,初始值为2
s_counting = xSemaphoreCreateCounting(3, 2);
xTaskCreate(task_C, "Task C", 512, NULL, 1, NULL);
xTaskCreate(task_D, "Task D", 512, NULL, 2, NULL);
// 启动调度器
vTaskStartScheduler();
return 0;
}
在这个示例中,我们创建了一个计数信号量s_counting
,两个任务(task_C
和task_D
)将会通过该信号量进行同步。s_counting
的最大值为3,初始值为2,从而保证同时最多只有两个任务可以获取该信号量。每个任务的执行方式类似,大致的流程如下:
- 尝试获取计数信号量
s_counting
; - 如果获取成功,说明该任务可以开始执行,用打印语句替代具体的任务处理,等待1秒;
- 释放计数信号量
s_counting
;
从示例中我们可以看出,只要计数信号量$s_counting$
还没有达到最大值,就可以任意多个任务获取该信号量,而达到最大值后,其他任务就无法获取该信号量,直到某个任务释放该信号量。
4. 进一步扩展: 二值信号量和计数信号量
在FreeRTOS中,二值信号量和计数信号量只是信号量的两种常见类型。如果我们需要更复杂的同步机制,可以通过扩展信号量来实现。以下是常见的信号量扩展方法:
- 二进制资源信号量(Binary Resource Semaphore):可以用于共享的硬件资源或软件资源等。在一些嵌入式系统中,CPU的某个时钟信号可能需要被多个模块共享,因此需要使用二进制资源信号量来管理共享。
- 优先级继承信号量(Priority Inheritance Semaphore):可以解决“优先级反转”(Priority Inversion)的问题。比如,如果高优先级任务需要等待低优先级任务占用的共享资源,而低优先级任务又正在等待其他资源,这时候就需要使用优先级继承信号量来提高高优先级任务的优先级,并且防止低优先级任务“抢占”共享资源而导致高优先级任务无限等待的问题。
- 读写信号量(Read-Write Semaphore):可以实现读者-写者问题。在一些多线程程序中,可能需要实现读操作和写操作的互斥,写者优先于读者、读者与读者之间不互斥等。这种情况下,可以使用读写信号量来解决问题。
扩展信号量需要根据具体情况,选择不同的实现方法。但基本的原则是要保证信号量的安全操作,避免死锁、优先级反转等问题的产生。
以上就是关于“FreeRTOS实时操作系统信号量基础”的完整攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:FreeRTOS实时操作系统信号量基础 - Python技术站