个人博客:blogs.wurp.top
简介
1. 多核支持概述
在传统的单核系统中,FreeRTOS 通常运行在一个 CPU 核心上,负责任务调度、中断处理和资源管理。然而,在多核系统中,多个核心可以并行执行不同的任务或线程,从而提升系统的性能和响应能力。
2. 多核支持方式
共享内存模型:多个核心共享同一块内存,通过锁机制协调访问。分布式模型:每个核心独立运行一个 FreeRTOS 实例,各自维护自己的任务队列和资源。
3. FreeRTOS典型应用场景
-
分布式计算任务:
每个核心运行独立的任务,分别处理不同的计算任务。通过队列或信号量协调任务之间的数据交换。
-
实时任务与非实时任务分离:
将实时任务分配给一个核心,非实时任务分配给另一个核心。使用互斥锁或信号量保证任务间的同步。
-
多核协同处理:
多个核心协同完成复杂任务(如图像处理、网络通信等)。通过共享内存或消息队列进行数据传输。
FreeRTOS多核架构
1. 单核与多核系统的区别
特性 | 单核系统 | 多核系统 |
---|---|---|
运行环境 | 一个 CPU 核心 | 多个 CPU 核心 |
任务调度 | 抢占式或时间片轮转 | 可以分配到不同核心 |
资源管理 | 单一核心管理 | 需要协调多个核心 |
通信与同步 | 简单 | 更加复杂 |
性能与效率 | 适合小规模应用 | 更高并发和性能 |
开发与调试 | 相对简单 | 更加复杂 |
2. FreeRTOS多核支持的实现方式
-
在 FreeRTOS 中,多核支持主要分为以下两种架构类型:
架构类型 描述 SMP(对称多处理) 所有核心共享相同的内存和资源,由同一个 FreeRTOS 内核管理。 AMP(异步多处理) 每个核心运行独立的 FreeRTOS 实例,彼此之间通过通信机制进行交互 -
FreeRTOS 支持多种多核平台,包括但不限于:
ARM Cortex-M7/M4/M3:支持 SMP 和 AMP 模式。
RISC-V:支持多核架构。
Xilinx Zynq UltraScale+ MPSoC:支持多核 FreeRTOS 配置。
TI AM335x 系列:支持多核 FreeRTOS 应用。
3. 多核任务调度机制
-
FreeRTOS 的任务调度是基于优先级的抢占式调度。在多核环境中,调度机制如下:
- 本地调度:每个核心维护自己的就绪任务列表,并根据优先级进行调度。
- 任务迁移:某些版本支持任务在不同核心之间迁移,以平衡负载。
- 中断处理:中断可以在任意核心上处理,但需要确保中断服务程序(ISR)的可重入性和同步。
多核支持的关键技术
1. 互斥锁(Mutex)
- 在多核环境中,多个核心可能会同时访问共享资源。为了防止竞态条件,FreeRTOS 提供了互斥锁(xSemaphoreCreateMutex())来确保同一时间只有一个核心可以访问 共享资源。
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();void vTaskFunction(void *pvParameters) {if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {// 访问共享资源xSemaphoreGive(xMutex);} }
2. 信号量(Semaphore)
- 信号量用于控制对共享资源的访问或任务之间的同步。FreeRTOS 提供了二值信号量(Binary Semaphore)、计数信号量(Counting Semaphore)和递归信号量 (Recursive Semaphore)。
SemaphoreHandle_t xBinarySemaphore = xSemaphoreCreateBinary();void vTaskFunction(void *pvParameters) {if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {// 任务执行逻辑xSemaphoreGive(xBinarySemaphore);} }
3. 队列(Queue)
- 队列是 FreeRTOS 中任务间通信的核心机制之一。在多核系统中,可以通过队列传递数据或事件,实现跨核通信。
QueueHandle_t xQueue = xQueueCreate(10, sizeof(int));void vTaskFunction(void *pvParameters) {int data = 42;xQueueSend(xQueue, &data, portMAX_DELAY); }
4. 任务调度器(Scheduler)
- 在多核系统中,FreeRTOS 可以配置为在多个核心上运行独立的调度器。每个核心的任务调度器独立运行,但可以通过共享资源进行协调。
// 启动任务调度器(适用于多核) vTaskStartScheduler();
FreeRTOS多核配置(以ARM Cortex-M7为例)
1. 硬件支持:确保使用的微控制器支持多核架构(如双核Cortex-M7)。
2. 内核配置:
- 启用configMULTI_CORE选项。
- 设置configNUM_CORES为实际的核数。
- 任务创建:
使用xTaskCreatePinnedToCore()函数将任务绑定到特定的核心。 - 资源同步:
使用xSemaphoreCreateBinary()或xQueueCreate()等机制进行任务间的通信。 - 中断配置:
将关键中断绑定到特定的核心,避免抢占问题。
3. 示例代码
#include "FreeRTOS.h"
#include "task.h"
void vTaskFunction(void *pvParameters) {while (1) {// 任务逻辑vTaskDelay(pdMS_TO_TICKS(1000));}
}
int main(void) {xTaskCreatePinnedToCore(vTaskFunction, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL, 0);xTaskCreatePinnedToCore(vTaskFunction, "Task2", configMINIMAL_STACK_SIZE, NULL, 1, NULL, 1);vTaskStartScheduler();for (;;);
}
多核任务调度
1. 任务分配策略
在多核系统中,合理的任务分配策略能够提升系统的性能和稳定性。常见的任务分配策略包括:
-
静态分配(Static Allocation)
任务在启动时就被分配到特定的核心。适用于对实时性要求高且任务数量固定的场景。
优点:可预测性强,减少任务迁移开销。
缺点:灵活性差,难以适应动态负载变化。
-
动态分配(Dynamic Allocation)
任务根据当前核心的负载情况动态分配。适用于负载不稳定的场景。
优点:提高资源利用率,优化系统性能。
缺点:可能增加任务迁移的开销。
-
基于优先级的分配(Priority-Based Allocation)
高优先级任务优先分配到空闲的核心。适用于对响应时间敏感的应用。
优点:保证关键任务的实时性。
缺点:可能导致低优先级任务长时间得不到执行。
-
基于负载的分配(Load-Balanced Allocation)
根据每个核心的负载情况动态分配任务。适用于多核系统中负载不均的场景。
优点:平衡各核心负载,提高整体性能。
缺点:需要额外的负载监控机制。
2. 负载均衡
-
目标:负载均衡的目标是将任务均匀地分配到各个核心上,避免某些核心过载而其他核心空闲,从而提高系统吞吐量和响应速度。
-
实现方式
动态任务迁移:根据各核心的负载情况,自动将任务迁移到较为空闲的核心上运行。任务优先级调整:通过调整任务优先级,确保高优先级任务能够及时得到处理。
负载监控:定期检查各核心的负载状态,为调度决策提供依据。
3. 任务优先级与抢占
-
任务优先级
在 FreeRTOS 中,每个任务都有一个优先级值,用于决定其调度顺序。优先级越高,任务越有可能被调度执行。
优先级范围
通常为 0 到 (configMAX_PRIORITIES - 1);0 表示最低优先级,configMAX_PRIORITIES - 1 表示最高优先级。
优先级设置xTaskCreate(vTaskFunction, // 任务函数"TaskName", // 任务名称STACK_SIZE, // 堆栈大小NULL, // 任务参数tskIDLE_PRIORITY + 1, // 任务优先级&xTaskHandle // 任务句柄 );
-
抢占机制
reeRTOS 支持 抢占式调度,即高优先级任务可以中断低优先级任务的执行。
抢占规则
当高优先级任务就绪时,它会立即抢占当前正在运行的低优先级任务。抢占行为仅在任务处于“就绪状态”时发生。
抢占模式
完全抢占:高优先级任务可以随时抢占低优先级任务。
部分抢占:某些情况下,抢占可能受限(如任务处于临界区)。
在多核环境中,任务的优先级和抢占行为仍然遵循上述规则,但可能会受到以下因素影响:
任务绑定:如果任务绑定到特定核心,则优先级和抢占仅在该核心内生效。核心间通信:跨核心的任务交互需要考虑同步机制,例如使用队列、信号量等。
4. 多核任务同步机制
同步机制 | 描述 |
---|---|
互斥锁(Mutex) | 用于保护共享资源,确保同一时间只有一个任务可以访问资源。 |
信号量(Semaphore) | 用于任务之间的通信,控制对共享资源的访问。 |
队列(Queue) | 用于任务之间传递数据或事件信息。 |
事件组(Event Group) | 用于任务之间传递多个事件的状态信息。 |
任务通知(Task Notification) | 一种轻量级的通信方式,用于任务之间的直接通知。 |
多核通信与同步
1. 消息队列与信号量
-
消息队列是一种用于任务间或中断服务程序与任务之间传递数据的机制。它支持先进先出(FIFO)或后进先出(LIFO)的数据传输方式。
QueueHandle_t xQueue;// 创建消息队列 xQueue = xQueueCreate(10, sizeof(uint32_t));// 发送消息 uint32_t data = 0x1234; xQueueSend(xQueue, &data, portMAX_DELAY);// 接收消息 uint32_t receivedData; xQueueReceive(xQueue, &receivedData, portMAX_DELAY);
-
信号量是一种用于控制对共享资源访问的同步机制。它可以用来实现任务间的同步、互斥访问等。
// 创建二值信号量 SemaphoreHandle_t xSemaphore = xSemaphoreCreateBinary();// 获取信号量(阻塞等待) xSemaphoreTake(xSemaphore, portMAX_DELAY);// 释放信号量 xSemaphoreGive(xSemaphore);
2. 共享内存与互斥锁
-
共享内存是一种允许多个任务或核心访问同一块内存区域的机制。在 FreeRTOS 中,通常通过以下方式实现共享内存:
使用全局变量或静态变量。
使用 Heap 区域分配的动态内存(如 pvPortMalloc())。
使用特定的内存映射机制(依赖于硬件平台)。
-
互斥锁是一种用于保护共享资源的同步机制,确保在同一时间只有一个任务可以访问受保护的代码段。
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();void TaskA(void *pvParameters) {while (1) {xSemaphoreTake(xMutex, portMAX_DELAY); // 获取互斥锁// 访问共享内存xSemaphoreGive(xMutex); // 释放互斥锁} }void TaskB(void *pvParameters) {while (1) {xSemaphoreTake(xMutex, portMAX_DELAY); // 获取互斥锁// 访问共享内存xSemaphoreGive(xMutex); // 释放互斥锁} }
3. 事件组与标志位
- 事件组是 FreeRTOS 中一种用于任务间通信和同步的机制,主要用于任务等待多个事件的发生,或者通知其他任务某个事件已经发生。
// 定义事件组 EventGroupHandle_t xEventGroup;// 初始化事件组 xEventGroup = xEventGroupCreate();// 任务 A:等待事件 void vTaskA(void *pvParameters) {EventBits_t uxBits;// 等待事件 0x01 和 0x02 同时发生uxBits = xEventGroupWaitBits(xEventGroup, 0x03, pdTRUE, pdTRUE, portMAX_DELAY);if ((uxBits & 0x01) && (uxBits & 0x02)){// 所有事件已触发} }// 任务 B:设置事件 void vTaskB(void *pvParameters) {// 设置事件 0x01xEventGroupSetBits(xEventGroup, 0x01);// 设置事件 0x02xEventGroupSetBits(xEventGroup, 0x02); }
- 标志位是事件组的一个子集,通常用于表示简单的状态变化。它比事件组更轻量,适合用于单个状态的标记。
// 定义标志位 volatile uint32_t ulFlag = 0;// 任务 A:等待标志位 void vTaskA(void *pvParameters) {while (1){if (ulFlag & 0x01){// 标志位 0x01 被设置ulFlag &= ~0x01; // 清除标志位}vTaskDelay(pdMS_TO_TICKS(100));} }// 任务 B:设置标志位 void vTaskB(void *pvParameters) {while (1){// 设置标志位 0x01ulFlag |= 0x01;vTaskDelay(pdMS_TO_TICKS(500));} }
4. 多核中断处理
- 在 FreeRTOS 中支持多核系统(如基于多核处理器的嵌入式设备),多核之间的通信和同步是实现任务协调的关键。
// 将任务绑定到核心 void vTaskFunction(void *pvParameters) {while (1) {// 任务逻辑} }// 创建任务并绑定到核心 xTaskCreatePinnedToCore(vTaskFunction, "Task", 2048, NULL, 1, NULL, 1);// 设置中断处理函数 void vMyInterruptHandler(void) {// 中断处理逻辑xTaskGenerateSoftwareInterrupt(0); // 触发任务 }
结论
FreeRTOS多核支持的优势
-
提高系统性能
多核架构允许同时执行多个任务,从而提升系统的整体性能。FreeRTOS 支持将任务分配到不同的核心上运行,充分利用多核处理器的计算能力。
-
增强实时性
在多核系统中,关键任务可以被分配到特定的核心上运行,避免因其他任务的干扰而影响实时响应。这有助于实现更精确的时序控制和更低的延迟。
-
任务隔离与安全性
多核支持使得不同任务可以在独立的内核上运行,减少任务之间的相互干扰。提高了系统的稳定性和安全性,尤其适用于需要高可靠性的应用。
-
灵活的任务调度
FreeRTOS 支持多种任务调度策略,并可根据多核架构进行优化。用户可以根据需求选择适合的调度方式,以适应不同的应用场景。
-
简化开发与维护
使用多核 FreeRTOS 可以通过模块化设计,将功能拆分到不同的核心上,便于开发和维护。开发者可以专注于各自核心上的功能实现,降低整体复杂度。
-
兼容现有生态
FreeRTOS 的多核支持与现有的 API 和工具链保持兼容,开发者无需重写大量代码即可利用多核优势。