下载 FreeRTOS
 

出色的 RTOS & 嵌入式软件

内核
最新资讯
简化任何设备的身份验证云连接。
利用 CoAP 设计节能型云连接 IoT 解决方案。
11.0.0 版 FreeRTOS 内核简介:
FreeRTOS 路线图和代码贡献流程。
使用 FreeRTOS 实现 OPC-UA over TSN。

FreeRTOS 的对称多处理 (SMP)

FreeRTOS 内核中的 SMP 支持可使一个 FreeRTOS FreeRTOS 内核实例在多个相同的处理器内核上调度任务。 这些内核架构必须相同,并共享相同的内存。

本页面包含:

FreeRTOS 和 SMP 入门指南

最简单的入门方法是使用以下预配置 的示例项目之一:


修改应用程序以使用 FreeRTOS SMP 功能

FreeRTOS API 单核和 SMP 版本之间基本上保持相同,除了 以下特定 API。因此,为尽量减少或不增加任何工作量,应同时编译 FreeRTOS 单核 与 SMP 版本。但是, 一些适用于单核应用的设计假设可能不适用于多核应用, 因此可能会存在一些功能问题。

其中一个常见的假定是,在较高优先级的任务正在运行时, 较低优先级的任务无法运行。虽然这在单核上是正确的,但并不适用于多核, 因为多个任务可以 同时运行。如果应用程序依赖于相对任务优先级来提供 相互排斥,则可能在多核环境中观察到意想不到的结果。 应用程序编写器有以下几个选择来解决这一问题:

  1. 最好的选择是更新应用程序,使其不依赖于任务优先级, 而是使用同步基元。
  2. 另一个选择是 使用 vTaskCoreAffinitySet API 将所有不能同时运行的任务固定到一个内核。
  3. 第 3 个选则是将 configRUN_MULTIPLE_priority 定义为 0, 这样可确保多个任务只有在具有相同优先级时 才会同时运行。请注意,这可能会导致利用率不足, 并使一些内核在可用于运行其他低优先级任务时处于空闲状态。

另一个常见的假定是, ISR 不能彼此或与其他任务同时运行。 这在多核环境中不再适用,应用程序编写器 需要在访问任务和 ISR 之间共享的数据时确保适当的互斥。可以在 ISR 中 使用宏 taskENTER_CRITICAL_FROM_ISR()taskEXIT_CRITICAL_FROM_ISR(), 并且可以在任务中使用宏 taskENTER_CRITICAL()taskEXIT_CRITICAL(), 以提供这种互斥。

SMP 特定 API

以下附加 API 可用于 FreeRTOS-SMP 内核:

xTaskCreateAffinitySet

BaseType_t xTaskCreateAffinitySet( TaskFunction_t pxTaskCode,
                                    const char * const pcName,
                                    const configSTACK_DEPTH_TYPE usStackDepth,
                                    void * const pvParameters,
                                    UBaseType_t uxPriority,
                                    UBaseType_t uxCoreAffinityMask,
                                    TaskHandle_t * const pxCreatedTask );

此函数是 xTaskCreate 的扩展, 用于创建一个带有关联掩码的新任务,并将其添加到准备运行的任务列表中。 configUSE_CORE_AFFINITY 必须定义为 1,才可使用此函数。

参数:

  • uxCoreAffinityMask - 一个按位值,指示可以 运行任务的内核。内核编号从 0 到 (configNUMBER_OF_CORES - 1)。例如, 为确保任务能在内核 0 和内核 1 上运行,请将 uxCoreAffinityMask 设置为 0x03。

xTaskCreateStaticAffinitySet

TaskHandle_t xTaskCreateStaticAffinitySet( TaskFunction_t pxTaskCode,
                                           const char * const pcName,
                                           const configSTACK_DEPTH_TYPE uxStackDepth,
                                           void * const pvParameters,
                                           UBaseType_t uxPriority,
                                           StackType_t * const puxStackBuffer,
                                           StaticTask_t * const pxTaskBuffer,
                                           UBaseType_t uxCoreAffinityMask );

此函数是 xTaskCreateStatic 的扩展, 用于创建一个带有关联掩码的新任务,并将其添加到准备运行的任务列表中。 configUSE_CORE_AFFINITY 必须定义为 1,才可使用此函数。

参数:

  • uxCoreAffinityMask - 一个按位值,指示可以 运行任务的内核。内核编号从 0 到 (configNUMBER_OF_CORES - 1)。例如, 可以在内核 0 和内核 1 上运行,请将 uxCoreAffinityMask 设置

xTaskCreateRestrictedAffinitySet

BaseType_t xTaskCreateRestrictedAffinitySet( const TaskParameters_t * const pxTaskDefinition,
                                             UBaseType_t uxCoreAffinityMask,
                                             TaskHandle_t * pxCreatedTask );

此函数是 xTaskCreateRestricted 的扩展, 用于创建带有关联掩码的内存保护单元 (MPU) 受限任务,并将其添加到任务列表中 configUSE_CORE_AFFINITY 必须定义为 1,此函数 才可用。

参数:

  • uxCoreAffinityMask - 一个按位值,指示可以 运行任务的内核。内核编号从 0 到 (configNUMBER_OF_CORES - 1)。例如, 为确保任务能在内核 0 和内核 1 上运行,请将 uxCoreAffinityMask 设置为 0x03。

xTaskCreateRestrictedStaticAffinitySet

BaseType_t xTaskCreateRestrictedStaticAffinitySet( const TaskParameters_t * const pxTaskDefinition,
                                                   UBaseType_t uxCoreAffinityMask,
                                                   TaskHandle_t * pxCreatedTask );

此函数是 xTaskCreateRestrictedStatic 的扩展, 并用于创建具有相关性掩码的内存保护单元(MPU)受限任务,并将其添加到准备运行的任务列表中。 configUSE_CORE_AFFINITY 必须定义为 1,此函数才可用 。

参数:

  • uxCoreAffinityMask - 一个按位值,指示可以 运行任务的内核。内核编号从 0 到 (configNUMBER_OF_CORES - 1)。例如, 可以在内核 0 和内核 1 上运行,请将 uxCoreAffinityMask 设置

vTaskCoreAffinitySet

void vTaskCoreAffinitySet( const TaskHandle_t xTask, UBaseType_t uxCoreAffinityMask ); 

configUSE_CORE_AFFINITY 必须定义为 1,才可使用此函数。

设置任务的内核关联掩码,即可以运行任务的内核。

参数:

  • xTask:内核关联掩码所要执行任务的句柄。传递 NULL 将为 调用任务设置内核关联掩码。

  • uxCoreAffinityMask - 一个按位值,指示可以 运行任务的内核。内核的编号范围为 0 到 (configNUMBER_OF_CORES - 1)。例如,为确保任务 可以在内核 0 和内核 1 上运行,请将 uxCoreAffinityMask 设置 为 0x03

用法示例:


/* The function that creates task. */
void vAFunction( void )
{
TaskHandle_t xHandle;
UBaseType_t uxCoreAffinityMask;

/* Create a task, storing the handle. */
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &( xHandle ) );

/* Define the core affinity mask such that this task can only run on core 0

* and core 2. */

uxCoreAffinityMask = ( ( 1 << 0 ) | ( 1 << 2 ) );

/* Set the core affinity mask for the task. */
vTaskCoreAffinitySet( xHandle, uxCoreAffinityMask );
}

vTaskCoreAffinityGet

UBaseType_t vTaskCoreAffinityGet( const TaskHandle_t xTask ); 

configUSE_CORE_AFFINITY 必须定义为 1,才可使用此函数。

设置任务的内核关联掩码,即可以运行任务的内核。

参数:

  • xTask:内核关联掩码所要执行任务的句柄。传递 NULL 将为 调用任务设置内核关联掩码。

返回:

  • 内核关联掩码,指示可以 运行任务的内核的按位值。内核编号从 0 到 (configNUMBER_OF_CORES - 1)。 例如,如果任务可以在内核 0 和内核 1 上运行,则内核关联掩码 为 0x03

用法示例:


/* Task handle of the networking task - it is populated elsewhere. */
TaskHandle_t xNetworkingTaskHandle;

void vAFunction( void )
{
TaskHandle_t xHandle;
UBaseType_t uxNetworkingCoreAffinityMask;

/* Create a task, storing the handle. */
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &( xHandle ) );

/* Get the core affinity mask for the networking task. */
uxNetworkingCoreAffinityMask = vTaskCoreAffinityGet( xNetworkingTaskHandle );

/* Here is a hypothetical scenario, just for the example. Assume that we

* have 2 cores - Core 0 and core 1. We want to pin the application task to

* the core that is not the networking task core to ensure that the

* application task does not interfere with networking. */

if( ( uxNetworkingCoreAffinityMask & ( 1 << 0 ) ) != 0 )
{
/* The networking task can run on core 0, pin our task to core 1. */
vTaskCoreAffinitySet( xHandle, ( 1 << 1 ) );
}
else
{
/* Otherwise, pin our task to core 0. */
vTaskCoreAffinitySet( xHandle, ( 1 << 0 ) );
}
}

vTaskPreemptionDisable

void vTaskPreemptionDisable( const TaskHandle_t xTask ); 

configUSE_TASK_PREEMPTION_DISABLE 必须定义为 1,才可使用此函数。

禁用抢占任务。

参数:

  • xTask:禁用抢占任务的句柄。传递 NULL 禁用调用任务的抢占。

用法示例:


void vTaskCode( void *pvParameters )
{
/* Silence warnings about unused parameters. */
( void ) pvParameters;

for( ;; )
{
/* ... Perform some function here. */

/* Disable preemption for this task. */
vTaskPreemptionDisable( NULL );

/* The task will not be preempted when it is executing in this portion ... */

/* ... until the preemption is enabled again. */
vTaskPreemptionEnable( NULL );

/* The task can be preempted when it is executing in this portion. */
}
}

vTaskPreemptionEnable

void vTaskPreemptionEnable( const TaskHandle_t xTask ); 

configUSE_TASK_PREEMPTION_DISABLE 必须定义为 1,才可使用此函数。

启用抢占任务。

参数:

  • xTask:启用抢占任务的句柄。传递 NULL 启用调用任务的抢占。

用法示例:


void vTaskCode( void *pvParameters )
{
/* Silence warnings about unused parameters. */
( void ) pvParameters;

for( ;; )
{
/* ... Perform some function here. */

/* Disable preemption for this task. */
vTaskPreemptionDisable( NULL );

/* The task will not be preempted when it is executing in this portion ... */

/* ... until the preemption is enabled again. */
vTaskPreemptionEnable( NULL );

/* The task can be preempted when it is executing in this portion. */
}
}

SMP 特定钩子和回调函数

被动空闲钩子函数:

FreeRTOS SMP 内核有两种类型的空闲任务:

  1. 空闲任务:单核 FreeRTOS 应用程序中使用的标准空闲任务。
  2. 被动空闲任务:有 (configNUMBER_OF_CORES - 1) 个被动空闲任务, 这些任务在空闲内核上运行,不执行任何操作。

被动空闲任务可以选择性地调用应用程序定义的钩子 (或回调)函数,即被动空闲钩子。被动空闲任务在 只有在无较高优先级任务运行时, 这种空闲钩子函数才会运行。

只有 configUSE_PASSIVE_IDLE_HOOKFreeRTOSConfig.h 中设置为 1 时,才会调用被动空闲钩子。设置后,应用程序必须为钩子函数 提供以下原型:

void vApplicationPassiveIdleHook( void ); 

只要被动空闲任务中的任何一个正在运行, 被动空闲任务就会重复调用被动空闲钩子。最重要的是,被动空闲钩子函数 不调用任何可能导致其阻塞的 API 函数。

被动空闲任务内存回调函数

如果 configSUPPORT_STATIC_ALLOCATION 设置为 1 ,则应用程序编写器需要提供者 vApplicationGetPassiveIdleTaskMemory,以提供供(configNUMBER_OF_CORES - 1)被动空闲任务使用的内存 。参见以下示例:


void vApplicationGetPassiveIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize
BaseType_t xPassiveIdleTaskIndex )
{
/* If the buffers to be provided to the Idle task are declared inside this

* function then they must be declared static - otherwise they will be allocated on

* the stack and so not exist after this function exits. */

static StaticTask_t xIdleTaskTCBs[ configNUMBER_OF_CORES - 1 ];
static StackType_t uxIdleTaskStacks[ configNUMBER_OF_CORES - 1 ][ configMINIMAL_STACK_SIZE ];

/* Pass out a pointer to the StaticTask_t structure in which the Idle task's

* state will be stored. */

*ppxIdleTaskTCBBuffer = &( xIdleTaskTCBs[ xPassiveIdleTaskIndex ] );

/* Pass out the array that will be used as the Idle task's stack. */
*ppxIdleTaskStackBuffer = &( uxIdleTaskStacks[ xPassiveIdleTaskIndex ][ 0 ] );

/* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.

* Note that, as the array is necessarily of type StackType_t,

* configMINIMAL_STACK_SIZE is specified in words, not bytes. */

*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}

或者,应用程序编写器可以在 FreeRTOSConfig.h 文件中将 configKERNEL_PROVIDED_STATIC_MEMORY 设置为 1,以使用内核提供的这些回调的默认实现。

SMP 特定配置选项

以下附加配置选项可用于 FreeRTOS-SMP 内核:

configNUMBER_OF_CORES

设置可用的处理器内核数。

configRUN_MULTIPLE_PRIORITIES

在单核 FreeRTOS 应用程序中, 如果存在能够运行的较高优先级任务,则较低优先级任务永远不会运行。 在 SMP FreeRTOS应用程序中, RTOS 内核将运行与可用内核数量一样多的任务,在一个内核上运行优先级较低的任务的同时, 较高优先级的任务可能同时 在另一个内核上运行。 如果您的应用程序或库 为单核环境编写,并且因此对任务执行的顺序做出假设, 这可能会导致问题。 因此,提供 configRUN_MULTIPLE_PRIORITIES 来 控制此行为。

如果 configRUN_MULTIPLE_PRIORITIES 定义 0,则多个任务 只有在具有相同优先级时才能同时运行, 保持这样的范例:如果存在能够运行的较高优先级任务,则较低优先级任务永远不会运行。 如果 configRUN_MULTIPLE_PRIORITIES 定义 1, 则具有不同优先级的多个任务可以同时运行,因此,较高和较低优先级的任务可以同时 在不同的内核上运行。

configUSE_CORE_AFFINITY

应用程序编写器可以控制任务在哪些内核上运行。 如果 configUSE_CORE_AFFINITY 定义为 1,则 vTaskCoreAffinitySet 可 控制任务在哪些内核上运行,vTaskCoreAffinityGet 可 用于查询任务在哪些内核上运行。 如果 configUSE_CORE_AFFINITY 为 0, 则 FreeRTOS 调度器可以在任何可用内核上自由运行任何任务。

configUSE_TASK_PREEMPTION_DISABLE

在单核 FreeRTOS 应用程序中,可以将 FreeRTOS 调度器配置为 抢占式或合作式——请参阅 configUSE_PREEMPTION 的定义。 在 SMP FreeRTOS 应用程序中,如果 configUSE_TASK_PREEMPTION_DISABLE 定义为 1, 则可使用 vTaskPreemptionDisablevTaskPreemptionEnable API 函数将单个任务设置为抢占式或协作式。

configUSE_PASSIVE_IDLE_HOOK

被动空闲任务钩子是在被动空闲任务的每个周期中调用的函数。应用程序编写器 需要执行以下操作才能创建被动空闲任务钩子:

  1. 在 FreeRTOSConfig.h 中将 configUSE_PASSIVE_IDLE_HOOK 设置为 1。
  2. 定义具有以下名称和原型的函数:
    void vApplicationPassiveIdleHook( void );

应用程序编写器可以使用被动空闲任务钩子来添加后台功能, 而无需单独任务的开销。

如果 configUSE_PASSIVE_IDLE_HOOK 未定义,则默认值为 0。

configTIMER_SERVICE_TASK_CORE_AFFINITY

configTIMER_SERVICE_TASK_CORE_AFFINITY 允许应用程序编写器在 SMP 系统上设置 RTOS 守护进程/定时器服务任务的核心相关性。

如果 configTIMER_SERVICE_TASK_CORE_AFFINITY 未定义,它将默认为 tskNO_AFFINITY

Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.