Mutex

目录

互斥量的基本概念

什么是互斥量

互斥量(Mutex)是用于保护共享资源的同步机制,确保在任何时刻只有一个任务可以访问临界资源。想象一下卫生间的钥匙:

  • 只有一个钥匙:同一时间只能有一个人使用卫生间
  • 谁拿钥匙谁用:拿到钥匙的人才能进入
  • 用完要归还:使用完毕后必须归还钥匙,其他人才能使用

互斥量的核心特性

所有权机制: - 只有获取互斥量的任务才能释放它 - 系统会记录当前持有互斥量的任务

优先级继承: - 当高优先级任务等待低优先级任务持有的互斥量时 - 低优先级任务会临时提升到高优先级 - 防止"优先级反转"问题

递归访问: - 同一个任务可以多次获取同一个互斥量 - 需要相同次数的释放操作才能彻底释放

互斥量 vs 二进制信号量

形象对比

特性 互斥量(Mutex) 二进制信号量(Binary Semaphore)
比喻 卫生间钥匙 停车场空位信号
所有权 有明确所有者 无所有者概念
释放限制 只能由获取者释放 任何任务都可以释放
优先级处理 支持优先级继承 无优先级保护
适用场景 保护共享资源 任务同步

技术对比

// 互斥量 - 用于资源保护
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();

void vTaskAccessResource(void) {
    if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
        // 访问共享资源
        access_shared_resource();
        // 必须由同一个任务释放
        xSemaphoreGive(xMutex);
    }
}

// 二进制信号量 - 用于任务同步
SemaphoreHandle_t xBinarySemaphore = xSemaphoreCreateBinary();

void vProducerTask(void) {
    // 生产数据...
    xSemaphoreGive(xBinarySemaphore);  // 通知消费者
}

void vConsumerTask(void) {
    xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);  // 等待生产者
    // 消费数据...
    // 不需要释放,因为用于同步
}

优先级反转问题详解

什么是优先级反转

优先级反转是实时系统中的经典问题,发生在不同优先级任务竞争同一资源时:

问题场景: 1. 低优先级任务L获取了互斥量 2. 中优先级任务M就绪,抢占了L 3. 高优先级任务H需要相同互斥量,被阻塞 4. 结果:高优先级任务H被迫等待中优先级任务M

互斥量的解决方案

互斥量通过优先级继承解决这个问题:

// 没有优先级继承的情况(使用二进制信号量)
void vLowPriorityTask(void) {
    xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);  // L获取信号量
    // 此时被中优先级任务M抢占
    // 高优先级任务H被阻塞,必须等待M和L都完成
    xSemaphoreGive(xBinarySemaphore);
}

// 有优先级继承的情况(使用互斥量)
void vLowPriorityTask(void) {
    xSemaphoreTake(xMutex, portMAX_DELAY);  // L获取互斥量
    // 当高优先级任务H等待时,L临时提升到H的优先级
    // L不会被M抢占,可以快速完成并释放互斥量
    xSemaphoreGive(xMutex);  // L恢复原有优先级
}

FreeRTOS互斥量创建函数

xSemaphoreCreateMutex - 创建互斥量

SemaphoreHandle_t xSemaphoreCreateMutex(void);

返回值: - 成功:互斥量句柄 - 失败:NULL(内存不足时)

创建示例

// 创建保护UART的互斥量
SemaphoreHandle_t xUartMutex;

void vInitMutexes(void) {
    xUartMutex = xSemaphoreCreateMutex();
    if(xUartMutex == NULL) {
        printf("ERROR: UART mutex creation failed!\n");
    } else {
        printf("UART mutex created successfully\n");
    }
}

xSemaphoreCreateMutexStatic - 静态创建

SemaphoreHandle_t xSemaphoreCreateMutexStatic(StaticSemaphore_t *pxMutexBuffer);

参数: - pxMutexBuffer:指向静态分配的内存块

静态创建示例

// 静态分配互斥量内存
static StaticSemaphore_t xUartMutexBuffer;

void vInitStaticMutex(void) {
    xUartMutex = xSemaphoreCreateMutexStatic(&xUartMutexBuffer);
}

xSemaphoreCreateRecursiveMutex - 创建递归互斥量

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);

递归互斥量特点: - 同一个任务可以多次获取 - 需要相同次数的释放操作 - 防止自死锁

互斥量操作函数

xSemaphoreTake - 获取互斥量

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, 
                         TickType_t xTicksToWait);

参数: - xSemaphore:互斥量句柄 - xTicksToWait:等待超时时间

返回值: - pdTRUE:成功获取互斥量 - pdFALSE:超时未获取到

使用模式

if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
    // 进入临界区,访问共享资源
    access_shared_resource();

    // 退出临界区
    xSemaphoreGive(xMutex);
} else {
    // 处理获取失败的情况
    handle_mutex_timeout();
}

xSemaphoreGive - 释放互斥量

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);

重要规则: - 必须由获取互斥量的任务释放 - 不能在中断服务程序中使用 - 递归互斥量需要匹配的释放次数

递归互斥量操作

xSemaphoreTakeRecursive

BaseType_t xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex,
                                  TickType_t xTicksToWait);

xSemaphoreGiveRecursive

BaseType_t xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex);

递归互斥量使用示例

void vComplexOperation(void) {
    // 第一次获取
    if(xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY) == pdTRUE) {
        // 调用其他可能也需要同一互斥量的函数
        vSubFunction();

        // 需要匹配的释放次数
        xSemaphoreGiveRecursive(xRecursiveMutex);
    }
}

void vSubFunction(void) {
    // 第二次获取(同一个任务)
    if(xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY) == pdTRUE) {
        // 执行操作...
        xSemaphoreGiveRecursive(xRecursiveMutex);
    }
}

互斥量删除函数

vSemaphoreDelete - 删除互斥量

void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);

使用注意事项: - 确保没有任务正在等待或持有互斥量 - 动态创建的互斥量才会释放内存 - 静态创建的互斥量只重置状态,不释放内存

删除示例

void vCleanupMutexes(void) {
    // 检查是否有任务持有互斥量
    if(uxSemaphoreGetCount(xUartMutex) == 0) {
        vSemaphoreDelete(xUartMutex);
        xUartMutex = NULL;
        printf("UART mutex deleted\n");
    } else {
        printf("Warning: Cannot delete mutex, it's currently held\n");
    }
}

互斥量查询函数

uxSemaphoreGetCount - 获取信号量计数

UBaseType_t uxSemaphoreGetCount(SemaphoreHandle_t xSemaphore);

对于互斥量的返回值: - 1:互斥量可用 - 0:互斥量已被占用

使用示例

void vCheckMutexStatus(void) {
    UBaseType_t uxCount = uxSemaphoreGetCount(xUartMutex);

    if(uxCount == 1) {
        printf("Mutex is available\n");
    } else {
        printf("Mutex is currently held by another task\n");
    }
}

互斥量综合实验案例:多任务共享资源保护

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "stdio.h"
#include "string.h"

// 共享资源 - 全局数据
typedef struct {
    int temperature;
    int humidity;
    int pressure;
    uint32_t update_count;
} SharedSensorData_t;

// 共享资源实例
static SharedSensorData_t xSharedData = {0};

// 互斥量句柄
SemaphoreHandle_t xDataMutex;        // 保护共享数据
SemaphoreHandle_t xDisplayMutex;     // 保护显示输出
SemaphoreHandle_t xFileMutex;        // 保护文件操作

// 传感器数据采集任务
void vSensorTask(void *pvParameters) {
    int task_id = (int)pvParameters;
    int local_temp, local_humid, local_press;

    printf("Sensor Task %d started\n", task_id);

    while(1) {
        // 模拟传感器读数
        local_temp = 20 + task_id + (rand() % 5);
        local_humid = 40 + task_id * 5 + (rand() % 10);
        local_press = 1000 + task_id * 10 + (rand() % 20);

        // 获取数据互斥量
        if(xSemaphoreTake(xDataMutex, 100 / portTICK_PERIOD_MS) == pdTRUE) {
            // 更新共享数据
            xSharedData.temperature = local_temp;
            xSharedData.humidity = local_humid;
            xSharedData.pressure = local_press;
            xSharedData.update_count++;

            printf("Sensor%d updated shared data (count: %lu)\n", 
                   task_id, xSharedData.update_count);

            // 释放互斥量
            xSemaphoreGive(xDataMutex);
        } else {
            printf("Sensor%d: Failed to get data mutex!\n", task_id);
        }

        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}

// 数据显示任务
void vDisplayTask(void *pvParameters) {
    SharedSensorData_t display_data;

    printf("Display Task started\n");

    while(1) {
        // 获取数据互斥量读取数据
        if(xSemaphoreTake(xDataMutex, portMAX_DELAY) == pdTRUE) {
            // 拷贝数据到局部变量
            memcpy(&display_data, &xSharedData, sizeof(SharedSensorData_t));
            xSemaphoreGive(xDataMutex);

            // 获取显示互斥量进行输出
            if(xSemaphoreTake(xDisplayMutex, 50 / portTICK_PERIOD_MS) == pdTRUE) {
                printf("\n=== Current Sensor Readings ===\n");
                printf("Temperature: %d°C\n", display_data.temperature);
                printf("Humidity:    %d%%\n", display_data.humidity);
                printf("Pressure:    %dhPa\n", display_data.pressure);
                printf("Update Count: %lu\n", display_data.update_count);
                printf("===============================\n");

                xSemaphoreGive(xDisplayMutex);
            }
        }

        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

// 数据记录任务(模拟文件操作)
void vDataLoggerTask(void *pvParameters) {
    SharedSensorData_t log_data;
    static uint32_t log_count = 0;

    printf("Data Logger Task started\n");

    while(1) {
        // 获取数据互斥量
        if(xSemaphoreTake(xDataMutex, portMAX_DELAY) == pdTRUE) {
            memcpy(&log_data, &xSharedData, sizeof(SharedSensorData_t));
            xSemaphoreGive(xDataMutex);

            // 获取文件互斥量(模拟文件操作)
            if(xSemaphoreTake(xFileMutex, 100 / portTICK_PERIOD_MS) == pdTRUE) {
                // 模拟文件写入操作
                printf("Log[%lu]: Writing data to file...\n", ++log_count);
                vTaskDelay(100 / portTICK_PERIOD_MS);  // 模拟写入时间
                printf("Log[%lu]: Data written successfully\n", log_count);

                xSemaphoreGive(xFileMutex);
            } else {
                printf("Logger: File busy, skipping log entry\n");
            }
        }

        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

// 系统监控任务
void vSystemMonitorTask(void *pvParameters) {
    printf("System Monitor Task started\n");

    while(1) {
        // 检查各个互斥量状态
        UBaseType_t data_status = uxSemaphoreGetCount(xDataMutex);
        UBaseType_t display_status = uxSemaphoreGetCount(xDisplayMutex);
        UBaseType_t file_status = uxSemaphoreGetCount(xFileMutex);

        printf("\n--- System Monitor ---\n");
        printf("Data Mutex:    %s\n", data_status ? "Available" : "In Use");
        printf("Display Mutex: %s\n", display_status ? "Available" : "In Use");
        printf("File Mutex:    %s\n", file_status ? "Available" : "In Use");
        printf("Shared Data Update Count: %lu\n", xSharedData.update_count);

        vTaskDelay(10000 / portTICK_PERIOD_MS);
    }
}

// 递归互斥量演示任务
void vRecursiveMutexDemoTask(void *pvParameters) {
    SemaphoreHandle_t xRecursiveMutex = xSemaphoreCreateRecursiveMutex();

    printf("Recursive Mutex Demo Task started\n");

    while(1) {
        // 第一次获取递归互斥量
        if(xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY) == pdTRUE) {
            printf("Recursive: First take\n");

            // 第二次获取(同一个任务)
            if(xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY) == pdTRUE) {
                printf("Recursive: Second take\n");

                // 执行一些操作
                vTaskDelay(100 / portTICK_PERIOD_MS);

                // 第一次释放
                xSemaphoreGiveRecursive(xRecursiveMutex);
                printf("Recursive: First give\n");
            }

            // 第二次释放
            xSemaphoreGiveRecursive(xRecursiveMutex);
            printf("Recursive: Second give - fully released\n");
        }

        vTaskDelay(8000 / portTICK_PERIOD_MS);
    }
}

// 优先级反转演示任务
void vPriorityInversionDemoTask(void *pvParameters) {
    int task_id = (int)pvParameters;

    printf("Priority Demo Task %d started\n", task_id);

    while(1) {
        if(task_id == 1) {  // 低优先级任务
            printf("LowPriority: Trying to get mutex\n");
            if(xSemaphoreTake(xDataMutex, portMAX_DELAY) == pdTRUE) {
                printf("LowPriority: Got mutex, working...\n");
                vTaskDelay(3000 / portTICK_PERIOD_MS);  // 长时间占用
                printf("LowPriority: Releasing mutex\n");
                xSemaphoreGive(xDataMutex);
            }
            vTaskDelay(10000 / portTICK_PERIOD_MS);

        } else if(task_id == 3) {  // 高优先级任务
            vTaskDelay(1000 / portTICK_PERIOD_MS);  // 让低优先级先运行
            printf("HighPriority: Trying to get mutex\n");
            TickType_t start_time = xTaskGetTickCount();

            if(xSemaphoreTake(xDataMutex, portMAX_DELAY) == pdTRUE) {
                TickType_t end_time = xTaskGetTickCount();
                printf("HighPriority: Got mutex after %lu ms\n", 
                       (end_time - start_time) * portTICK_PERIOD_MS);
                xSemaphoreGive(xDataMutex);
            }
            vTaskDelay(15000 / portTICK_PERIOD_MS);
        }
    }
}

// 系统初始化
void vInitMutexSystem(void) {
    // 创建所有互斥量
    xDataMutex = xSemaphoreCreateMutex();
    xDisplayMutex = xSemaphoreCreateMutex();
    xFileMutex = xSemaphoreCreateMutex();

    if(xDataMutex == NULL || xDisplayMutex == NULL || xFileMutex == NULL) {
        printf("ERROR: Mutex creation failed!\n");
        while(1);
    }

    printf("All mutexes created successfully\n");
}

// 主函数
int main(void) {
    printf("Starting Mutex Demonstration System...\n");

    // 初始化互斥量系统
    vInitMutexSystem();

    // 创建传感器任务(3个)
    xTaskCreate(vSensorTask, "Sensor1", 1024, (void*)1, 1, NULL);
    xTaskCreate(vSensorTask, "Sensor2", 1024, (void*)2, 1, NULL);
    xTaskCreate(vSensorTask, "Sensor3", 1024, (void*)3, 1, NULL);

    // 创建处理任务
    xTaskCreate(vDisplayTask, "Display", 1024, NULL, 2, NULL);
    xTaskCreate(vDataLoggerTask, "Logger", 1024, NULL, 2, NULL);
    xTaskCreate(vSystemMonitorTask, "Monitor", 1024, NULL, 1, NULL);
    xTaskCreate(vRecursiveMutexDemoTask, "Recursive", 1024, NULL, 1, NULL);

    // 创建优先级演示任务
    xTaskCreate(vPriorityInversionDemoTask, "LowPriority", 1024, (void*)1, 1, NULL);
    xTaskCreate(vPriorityInversionDemoTask, "MidPriority", 1024, (void*)2, 2, NULL);
    xTaskCreate(vPriorityInversionDemoTask, "HighPriority", 1024, (void*)3, 3, NULL);

    // 启动调度器
    printf("Starting FreeRTOS scheduler...\n");
    vTaskStartScheduler();

    return 0;
}

互斥量使用最佳实践

设计原则

保持临界区简短

// 好的做法:只保护共享数据访问
if(xSemaphoreTake(xMutex, timeout) == pdTRUE) {
    // 快速操作:只拷贝数据或更新变量
    shared_variable = new_value;
    xSemaphoreGive(xMutex);
}
// 耗时操作放在临界区外
process_data(shared_variable);

// 不好做法:在临界区内执行耗时操作
if(xSemaphoreTake(xMutex, timeout) == pdTRUE) {
    // 这会长时间阻塞其他任务
    long_processing_operation();
    xSemaphoreGive(xMutex);
}

避免嵌套死锁

// 危险:可能产生死锁
void vFunctionA(void) {
    xSemaphoreTake(xMutex1, portMAX_DELAY);
    xSemaphoreTake(xMutex2, portMAX_DELAY);  // 如果其他任务以相反顺序获取,会产生死锁
    // ...
    xSemaphoreGive(xMutex2);
    xSemaphoreGive(xMutex1);
}

// 安全:固定获取顺序
void vFunctionA(void) {
    xSemaphoreTake(xMutex1, portMAX_DELAY);
    xSemaphoreTake(xMutex2, portMAX_DELAY);
    // ...
    xSemaphoreGive(xMutex2);
    xSemaphoreGive(xMutex1);
}

void vFunctionB(void) {
    xSemaphoreTake(xMutex1, portMAX_DELAY);  // 同样顺序
    xSemaphoreTake(xMutex2, portMAX_DELAY);
    // ...
    xSemaphoreGive(xMutex2);
    xSemaphoreGive(xMutex1);
}

错误处理策略

超时处理

BaseType_t xResult = xSemaphoreTake(xMutex, reasonable_timeout);
if(xResult != pdTRUE) {
    // 处理策略:
    // 1. 使用默认值继续执行
    // 2. 跳过本次操作
    // 3. 尝试恢复或重置系统
    handle_mutex_timeout();
    return;
}

资源清理

void vCriticalOperation(void) {
    if(xSemaphoreTake(xMutex, timeout) == pdTRUE) {
        // 确保在任何退出路径都释放互斥量
        if(operation_failed) {
            xSemaphoreGive(xMutex);
            return;
        }

        // 正常操作...
        xSemaphoreGive(xMutex);
    }
}