引言在嵌入式实时操作系统RTOS中多个任务共享资源如串口、全局变量、外设等时必须采取同步机制防止数据错乱。互斥量Mutex是专门为解决“互斥访问”而设计的同步对象它不仅能保护共享资源还具有优先级继承特性能有效缓解优先级反转问题。本文将基于一个 STM32 FreeRTOSCMSIS‑RTOS V2的实际工程通过三个不同优先级的任务争夺同一个互斥量直观演示任务的调度顺序、互斥量的保护作用以及优先级继承的触发过程。代码全部开源可直接在开发板上运行测试。一、工程代码展示下面的代码是从freertos.c中摘取的核心部分包含三个任务高、中、低优先级和一个互斥量。低优先级任务长时间占用互斥量高优先级任务等待互斥量中优先级任务仅做打印展示。/* 包含头文件 */ #include FreeRTOS.h #include task.h #include main.h #include cmsis_os.h #include stdio.h /* 互斥量句柄和属性 */ osMutexId_t Mutex01Handle; const osMutexAttr_t Mutex01_attributes { .name Mutex01 }; /* 任务句柄和属性 */ osThreadId_t task_HHandle, task_MHandle, task_LHandle; const osThreadAttr_t task_H_attributes { .name task_H, .stack_size 128*4, .priority osPriorityLow2 }; const osThreadAttr_t task_M_attributes { .name task_M, .stack_size 128*4, .priority osPriorityLow1 }; const osThreadAttr_t task_L_attributes { .name task_L, .stack_size 128*4, .priority osPriorityLow }; /* 任务函数声明 */ void Task_H_Func(void *argument); void Task_M_Func(void *argument); void Task_L_Func(void *argument); /* 初始化函数中创建互斥量和任务 */ void MX_FREERTOS_Init(void) { Mutex01Handle osMutexNew(Mutex01_attributes); // 创建互斥量 task_HHandle osThreadNew(Task_H_Func, NULL, task_H_attributes); task_MHandle osThreadNew(Task_M_Func, NULL, task_M_attributes); task_LHandle osThreadNew(Task_L_Func, NULL, task_L_attributes); } /* 高优先级任务获取互斥量占用1秒释放然后延迟1秒 */ void Task_H_Func(void *argument) { for (;;) { osMutexAcquire(Mutex01Handle, osWaitForever); printf(High Task get mutex, start\r\n); HAL_Delay(1000); // 模拟占用资源 printf(High Task give mutex, end\r\n); osMutexRelease(Mutex01Handle); osDelay(1000); // 主动让出CPU } } /* 中优先级任务只打印不访问互斥量 */ void Task_M_Func(void *argument) { for (;;) { printf(Middle Task use cpu, but do nothing\r\n); osDelay(1000); } } /* 低优先级任务获取互斥量占用3秒释放延迟1秒 */ void Task_L_Func(void *argument) { for (;;) { osMutexAcquire(Mutex01Handle, osWaitForever); printf(Low Task get mutex, start\r\n); HAL_Delay(3000); printf(Low Task give mutex, end\r\n); osMutexRelease(Mutex01Handle); osDelay(1000); } }注代码中默认还有一个defaultTask用于翻转 LED不影响互斥量演示故未完全列出。二、关键知识点1. 任务优先级CMSIS‑RTOS V2 中优先级数值越大优先级越高osPriorityLow 1osPriorityLow1 2osPriorityLow2 3因此task_H (3) task_M (2) task_L (1)。2. 互斥量 vs 二进制信号量特性互斥量 (Mutex)二进制信号量 (Semaphore)用途保护共享资源互斥访问任务同步 / 事件通知优先级继承支持不支持谁可以释放只能由获取者释放任何任务或中断均可释放适用场景资源保护轻量级同步本代码中使用互斥量并在注释中保留了二进制信号量的创建但实际并未使用。3. 优先级反转与优先级继承优先级反转高优先级任务等待低优先级任务持有的资源而中优先级任务不访问资源抢占 CPU导致高优先级任务迟迟无法运行。优先级继承当高优先级任务等待低优先级任务持有的互斥量时低优先级任务会临时提升到高优先级任务的级别防止中优先级任务抢占从而缩短高优先级任务的等待时间。三、代码运行行为分析场景模拟启动后三个任务均就绪调度器优先运行task_H。task_H获取互斥量 → 打印 →HAL_Delay(1000)忙等 → 释放互斥量 → 打印 →osDelay(1000)阻塞。task_H 阻塞后调度器运行次高优先级任务task_M→ 打印 →osDelay(1000)阻塞。task_M 阻塞后调度器运行task_L→ 获取互斥量 → 打印 → 开始HAL_Delay(3000)。在 task_L 忙等期间1秒后 task_H 从osDelay中唤醒立即抢占 CPU。task_H 尝试获取互斥量→ 被 task_L 持有 → task_H 进入阻塞态等待互斥量。优先级继承生效task_L 的优先级被临时提升到与 task_H 相同osPriorityLow2。task_L 继续执行完HAL_Delay(3000)→ 释放互斥量 → task_H 获得互斥量并继续执行。实际串口输出示例注意如果使用二进制信号量代替互斥量则不会发生优先级继承中优先级任务可能在低优先级任务持有锁期间抢占 CPU导致高优先级任务等待时间无限延长。四、常见疑问解答Q1为什么 task_H 抢占后没有立即打印“High Task get mutex”A因为 task_H 抢占后马上执行osMutexAcquire发现互斥量已被 task_L 占用于是立即进入阻塞态不会执行到printf。只有当 task_L 释放互斥量task_H 获得锁后才会打印High Task get mutex, start。Q2如果我把 task_H 的osDelay(1000)改为HAL_Delay(1000)会怎样AHAL_Delay是忙等待不会让出 CPU。那么 task_H 释放互斥量后会继续忙等 1 秒期间仍然占用 CPU导致 task_M 和 task_L 得不到运行。这相当于高优先级任务“霸占”CPU系统实时性变差。推荐使用osDelay主动阻塞。Q3如果将互斥量换成二进制信号量代码需要修改哪里A将osMutexAcquire/Release替换为osSemaphoreAcquire/Release并删除互斥量创建改用osSemaphoreNew(1,1,NULL)。但二进制信号量会导致优先级反转不推荐用于资源保护。五、总结知识点本示例体现互斥量的创建osMutexNew(Mutex01_attributes)获取/释放osMutexAcquire/osMutexRelease任务优先级osPriorityLow2osPriorityLow1osPriorityLow阻塞与唤醒osDelay让出 CPUosMutexAcquire等待锁时阻塞优先级继承task_L 持有锁时被临时提升避免被 task_M 抢占临界区保护对printf和HAL_Delay的访问被互斥量保护不会交错通过本例您可以掌握 FreeRTOS 互斥量的基本用法理解优先级继承的实际效果并能够在自己的项目中正确使用互斥量保护共享资源。