FreeRTOS SMP 内核单元测试详解:结构与测试组设计指南

FreeRTOS SMP 内核单元测试详解:结构与测试组设计指南 FreeRTOS SMP 内核单元测试详解结构与测试组设计指南【免费下载链接】FreeRTOSClassic FreeRTOS distribution. Started as Git clone of FreeRTOS SourceForge SVN repo. Submodules the kernel.项目地址: https://gitcode.com/GitHub_Trending/fr/FreeRTOS导读本指南以 FreeRTOS/Test/CMock/smp/README.md 为核心系统讲解 FreeRTOS 内核中对称多处理SMP调度器逻辑的单元测试方案。文章将带你理解测试目录的组织方式、测试组如何依据configRUN_MULTIPLE_PRIORITIES与configUSE_TIME_SLICING的组合划分、功能测试与覆盖测试的区别与命名约定并结合仓库中的公共测试基础设施mock、stub 回调、TCB 辅助构造说明每个测试组如何验证tasks.c中configNUMBER_OF_CORES 1保护的 SMP 专属代码路径。读完本文你将掌握 SMP 单元测试的目录结构、用例编写规范、配置矩阵设计思路以及如何在本地构建与运行这些测试。SMP 单元测试的目标与边界FreeRTOS 内核的单元测试位于 FreeRTOS/Test/CMock其中smp目录下的测试专门负责验证 tasks.c 中由configNUMBER_OF_CORES 1条件编译包围的SMP 调度器逻辑例如多核就绪队列选择、多核 Idle 任务管理、按核维护的 yield 状态、核间临界区spinlock等。值得注意的是测试范围的边界划分SMP 专属逻辑由smp测试组验证单核与 SMP 共享的公共调度器逻辑如任务创建、延时列表管理、普通就绪态切换仍在 FreeRTOS/Test/CMock/tasks 中验证。这种按编译开关切分验证范围的做法保证了共享代码与 SMP 专属代码各有明确、不重复的测试归属。目录结构与测试组命名规则smp测试的目录结构如下源自 README与实际仓库一致FreeRTOS/Test/CMock/smp ├── Makefile ├── config_assert │ └── config_assert_utest.c ├── multiple_priorities_no_timeslice_mock │ └── covg_multiple_priorities_no_timeslice_mock_utest.c ├── multiple_priorities_no_timeslice │ ├── covg_multiple_priorities_no_timeslice_utest.c │ └── multiple_priorities_no_timeslice_utest.c ├── multiple_priorities_timeslice │ ├── covg_multiple_priorities_timeslice_utest.c │ └── multiple_priorities_timeslice_utest.c ├── single_priority_no_timeslice │ ├── covg_single_priority_no_timeslice_utest.c │ └── single_priority_no_timeslice_utest.c ├── single_priority_timeslice │ ├── covg_single_priority_timeslice_utest.c │ └── single_priority_timeslice_utest.c ├── global_vars.h ├── smp_utest_common.c └── smp_utest_common.h组织原则每个文件夹代表一个测试组test group具有相同 FreeRTOSConfig.h 配置的测试用例被归入同一测试组。顶层 Makefile 通过SUITES变量显式登记这些测试组SUITES single_priority_no_timeslice SUITES single_priority_timeslice SUITES multiple_priorities_no_timeslice SUITES multiple_priorities_timeslice SUITES multiple_priorities_no_timeslice_mock SUITES config_assertPROJECT与SUITE变量根据路径自动推导$(UT_ROOT_DIR)/$(PROJECT)/$(SUITE)随后 include../subdir.mk完成统一构建。测试组划分双配置维度的正交组合测试组围绕两个 SMP 相关配置宏的正交组合创建配置宏含义configRUN_MULTIPLE_PRIORITIES是否允许多个不同优先级的任务同时在不同核上运行1 时多核可并行调度不同优先级任务configUSE_TIME_SLICING是否启用时间片轮转调度同优先级任务在核间轮转由此生成四个基本测试组single_priority_timeslice单优先级 时间片single_priority_no_timeslice单优先级 无时间片multiple_priorities_timeslice多优先级 时间片multiple_priorities_no_timeslice多优先级 无时间片以 single_priority_timeslice/FreeRTOSConfig.h 为例其 SMP 专属配置为#define configRUN_MULTIPLE_PRIORITIES 0 #define configNUMBER_OF_CORES 16 #define configUSE_CORE_AFFINITY 1 #define configUSE_TIME_SLICING 1 #define configUSE_TASK_PREEMPTION_DISABLE 0 #define configTICK_CORE 0而 multiple_priorities_no_timeslice_mock/FreeRTOSConfig.h 则采用configRUN_MULTIPLE_PRIORITIES 1、configUSE_TIME_SLICING 0并额外开启configUSE_TASK_PREEMPTION_DISABLE、configUSE_TICKLESS_IDLE以覆盖不同特性组合下的调度路径。测试环境的configNUMBER_OF_CORES被设为 16意味着所有用例都必须在任意大于 1 的核心数下成立从而保证测试对多核配置的普适性。补充测试组mock 变体与 configASSERT在四个基本组合之外还有两个特殊测试组multiple_priorities_no_timeslice_mock为了提高测试覆盖率该组对 list.h 中的 API 进行 mock从目录中的list_macros.h与local_portable.h可以看出其引入了本地可移植层与列表宏的替身配置与multiple_priorities_no_timeslice类似但允许测试直接控制列表操作的行为从而覆盖难以在真实列表上触达的分支。config_assert专门覆盖 SMP 调度器逻辑中的configASSERT断言路径验证非法状态下内核能够正确触发断言。功能测试与覆盖测试命名约定与职责划分每个测试组包含两类测试用例通过源文件名区分覆盖测试Coverage testcovg_test_group_name_utest.c功能测试Functional testtest_group_name_utest.c功能测试验证调度行为符合功能需求功能测试用例在注释中明确写出要验证的功能需求、测试步骤和预期结果。README 给出的经典用例AWS_IoT-FreeRTOS_SMP_TC-1完整地展示了这一规范——在configRUN_MULTIPLE_PRIORITIES 0、configUSE_TIME_SLICING 0、configNUMBER_OF_CORES 1时创建 N 个等优先级任务后启动调度器预期每个核上都各有一个任务处于 Running 状态void test_priority_verification_tasks_equal_priority( void ) { TaskHandle_t xTaskHandles[ configNUMBER_OF_CORES ] { NULL }; uint32_t i; /* Create configNUMBER_OF_CORES tasks of equal priority */ for( i 0; i configNUMBER_OF_CORES; i ) { xTaskCreate( vSmpTestTask, SMP Task, configMINIMAL_STACK_SIZE, NULL, 1, xTaskHandles[ i ] ); } vTaskStartScheduler(); /* Verify all configNUMBER_OF_CORES tasks are in the running state */ for( i 0; i configNUMBER_OF_CORES; i ) { verifySmpTask( xTaskHandles[ i ], eRunning, i ); } }这里的关键验证点是verifySmpTask()实现在 smp_utest_common.c它通过vTaskGetInfo()读取任务信息同时断言任务的eCurrentState eRunning以及xTaskRunState i第 i 个任务正运行在第 i 个核上从而确认每个核各跑一个等优先级任务这一 SMP 核心行为。覆盖测试定点验证调度器代码路径覆盖测试用例则针对 SMP 调度器逻辑中的特定代码路径在注释中指明要覆盖的源码行并尽可能使用 mock 函数以最小化依赖。README 给出的xTaskResumeFromISR覆盖示例非常典型——它直接在测试中构造 TCB 数组、初始化xSuspendedTaskList与xPendingReadyList逐个核设置xTaskRunState、xYieldPendings和pxCurrentTCBs随后将挂起任务恢复验证其返回值xReturn pdTRUEvoid test_coverage_xTaskResumeFromISR_resume_higher_priority_suspended_task( void ) { TCB_t xTaskTCBs[ configNUMBER_OF_CORES 1U ] { NULL }; ... /* A suspended task is created to be resumed from ISR. The task has higher * priority than uxTopReadyPriority and the scheduler is suspended. */ xTaskTCBs[ configNUMBER_OF_CORES ].uxPriority 2; listINSERT_END( xSuspendedTaskList, xTaskTCBs[ i ].xStateListItem ); uxTopReadyPriority 1; uxSchedulerSuspended pdTRUE; xReturn xTaskResumeFromISR( xTaskTCBs[ i ] ); /* In single priority test, the calling core is requested to yield * since a higher priority task is resumed. */ TEST_ASSERT( xReturn pdTRUE ); }其注释中明确标注了覆盖目标prvYieldForTask( pxTCB )及xYieldPendings[ portGET_CORE_ID() ] ! pdFALSE分支为真体现了一用例一目标分支的覆盖测试纪律。测试公共基础设施smp_utest_common 的支撑作用所有测试组共享 smp_utest_common.h 与 smp_utest_common.c后者是测试能否在宿主host环境模拟多核行为的关键。内存与调度器替身pvPortMalloc/vPortFree重定向到 Unity 的内存分配器unity_malloc/unity_free使内核内存管理在测试中可控、可追踪xPortStartScheduler的 mock 实现会在启动时为每个核调用一次vTaskSwitchContext( i )模拟多核同时启动的初始状态。核 ID 与核间 yield 的模拟由于宿主机没有真实的多核硬件测试用静态变量模拟每个核的状态xCurrentCoreId记录portGET_CORE_ID()的返回值vSetCurrentCore()可在调用 FreeRTOS API 前切换当前核xTaskLockCount[]/xIsrLockCount[]分别模拟每个核的任务锁与 ISR 锁计数即临界区的 spinlock 语义xCoreYields[]记录各核的异步 yield 请求。以vFakePortYieldCoreStubCallback为例它模拟portYIELD_CORE若发现任何核仍持有锁处于临界区则将 yield 挂起至锁释放否则切换到目标核执行vTaskSwitchContext。释放锁的回调vFakePortReleaseTaskLockCallback在锁计数归零后会调用vYieldCores()依次处理等待中的 yield 请求从而完整复刻临界区内挂起 yield、出临界区后统一执行的内核行为。静态 TCB 构造辅助vCreateStaticTestTask()允许测试直接构造 TCB 并挂接pxCurrentTCBs[xTaskRunState]同时维护uxCurrentNumberOfTasksvCreateStaticTestTaskAffinity()在此基础上额外设置uxCoreAffinityMask在configUSE_CORE_AFFINITY 1时可用。这些辅助函数让覆盖测试可以在不经过xTaskCreate全流程的情况下精确布置调度器运行所需的初始数据结构。公共 TCB 与常量定义global_vars.h 定义了测试所需的完整 TCB 结构TCB_t以及关键常量其中与 SMP 直接相关的有xTaskRunState标识任务运行在哪个核或处于未运行taskTASK_NOT_RUNNING/ 让位中taskTASK_YIELDING状态uxTaskAttributes与taskATTRIBUTE_IS_IDLE用于标识 Idle 任务每个核一份的pxCurrentTCBs[configNUMBER_OF_CORES]与xYieldPendings[configNUMBER_OF_CORES]。config_assert 测试组用 CException 捕获断言config_assert测试组config_assert_utest.c用于验证 SMP 调度器中的configASSERT行为。由于断言宏在测试环境中被替换为可注入的vFakeAssert见各 FreeRTOSConfig.h 中基于fake_assert.h的configASSERT定义测试可以用 CException 捕获断言触发点然后验证异常码#define EXPECT_ASSERT_BREAK( call ) \ do \ { \ shouldAbortOnAssertion true; \ CEXCEPTION_T e CEXCEPTION_NONE; \ Try \ { \ call; \ TEST_FAIL(); \ } \ Catch( e ) \ { \ TEST_ASSERT_EQUAL( configASSERT_E, e ); \ } \ } while( 0 )EXPECT_ASSERT_BREAK宏的语义是若被调用函数未触发断言即call正常返回则用例直接TEST_FAIL()若触发断言则由Catch分支验证异常码为configASSERT_E。该组通过mock_list.h、mock_local_portable.h等 mock 依赖隔离外部干扰专门覆盖调度器在非法状态如核间状态不一致下必须失败的防御路径。CMock 配置mock 生成规则所有 mock 由 smp.yml 统一驱动生成关键配置包括mock_prefix: mock_mock 函数命名前缀:plugins: [:ignore, :ignore_arg, :expect_any_args, :callback, :return_thru_ptr, :cexception]启用参数忽略、回调与 CException 支持——这正是_StubWithCallback系列与EXPECT_ASSERT_BREAK得以工作的基础:attributes: [PRIVILEGED_FUNCTION]与:strippables: [PRIVILEGED_FUNCTION, portDONT_DISCARD, ...]剥离内核源码中与测试无关的编译器属性与宏:weak: __attribute__((weak))生成弱符号 mock便于替换。如何构建与运行 SMP 单元测试make依赖与统一运行方式记录在 FreeRTOS/Test/CMock/Readme.md依赖包括 GCC、unifdef、LCOV、GNU Make、Ruby覆盖率过滤需 Python 3.8 与 GNU cflow。在 CMock 根目录可执行$ make run # 构建全部单元测试并依次运行 $ make run_col # 彩色输出便于快速定位失败用例 $ make coverage # 运行全部测试并生成 HTML 覆盖率报告到 build/coverage针对单个测试目录如 SMP 套件可以像其他模块make -C list一样直接构建运行$ make -C FreeRTOS/Test/CMock/smp $ make -C FreeRTOS/Test/CMock/smp gcov $ make -C FreeRTOS/Test/CMock/smp lcovhtml顶层 smp/Makefile 已通过SUITES汇总全部 6 个测试组因此一次构建即可覆盖四象限组合、mock 变体与断言测试。需要内存检查时可在 make 时追加ENABLE_SANITIZER1启用 GCC Address Sanitizer——注意 sanitizer 会引入额外分支可能影响覆盖率采集因此默认不开启推荐在新增或修改用例时使用。小结FreeRTOS SMP 单元测试以配置矩阵 × 测试类型双维度组织四组功能/覆盖测试覆盖configRUN_MULTIPLE_PRIORITIES与configUSE_TIME_SLICING的四种组合mock 变体提升列表操作的覆盖率config_assert组专门守护断言路径。通过 smp_utest_common.c 对核 ID、核间锁、yield 请求的软件模拟以及 global_vars.h 提供的 TCB 级数据布局测试得以在宿主环境精确复现多核调度语义——这套边界明确、命名规范、分层复用的测试设计既是 FreeRTOS SMP 调度器质量的保障也是嵌入式内核单元测试工程化的参考范本。【免费下载链接】FreeRTOSClassic FreeRTOS distribution. Started as Git clone of FreeRTOS SourceForge SVN repo. Submodules the kernel.项目地址: https://gitcode.com/GitHub_Trending/fr/FreeRTOS创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考