FreeRTOS 互斥信号量(互斥锁)

本文介绍了FreeRTOS中互斥信号量的使用,重点讲解了它如何通过优先级继承机制降低优先级翻转的影响,以及相关的API函数创建、释放和获取操作。实验部分展示了在实际任务调度中互斥信号量的有效应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、互斥信号量

互斥信号量其实就是一个拥有优先级继承的二值信号量,多用于不同任务间对共享资源的互斥访问。

2、互斥信号量降低优先级翻转的影响

当一个互斥信号量正在被任务L使用,而此时有个任务H也尝试获取这个互斥信号量的话就会被阻塞。不过这个任务H会将任务L的优先级提升到与自己相同的优先级, 这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的“优先级翻转”的影响降到最低。

序号

说明

(1)

任务H和任务M为阻塞状态,等待某一事件发生,此时任务L正在运行

(2)

此时任务L要访问共享资源,需要获取互斥锁,同时系统将L的优先级拉至与H一样

(3)

任务L成功获取互斥锁,此时锁已被占用,任务L开始访问共享资源

(4)

时间片轮循时间到,此时任务H就绪,开始运行

(5)

任务H开始运行

(6)

此时任务H要访问共享资源,因此需要获取锁,但此时锁未释放,因此任务H阻塞等待互斥锁释放

(7)

由于L与H此时优先相同,且大于M,所以任务L继续运行

(8)

任务L释放互斥锁

(9)

任务H获取到互斥锁资源,解除阻塞开始运行,此时L的优先级又回到原值

(10)

任务H运行完毕,进入阻塞

(11)

任务M此时是就绪列表里面优先级最高的开始运行

(12)

任务M运行完毕,进入阻塞

(13)

任务L开始新一轮的运行

3、互斥信号量的API函数

3.1、创建

SemaphoreHandle_t xSemaphoreCreateMutex(void)

  • 创建互斥信号量

参数

说明

void

返回值

说明

NULL

创建互斥信号量失败

其他值

创建互斥信号量成功

3.2、释放

与二值信号量一样,请参考二值信号量释放函数

3.3、获取

与二值信号量一样,请参考二值信号量释放函数

4、实验

4.1、实验设计

实验设计跟“优先级翻转”章节流程一样,只是将“二值信号量”换成了“互斥信号量”。

4.2、实验代码

//任务1
k_task_handle_t task1_handle;           //任务1 句柄
#define TSK1_PRIO            2         //任务1 优先级
#define TASK1_STK_SIZE       (1*512)   //任务1 分配的堆栈大小

//任务2
k_task_handle_t task2_handle;           //任务2 句柄
#define TSK2_PRIO            3         //任务2 优先级
#define TASK2_STK_SIZE       (1*512)   //任务2 分配的堆栈大小

//任务3
k_task_handle_t task3_handle;           //任务3 句柄
#define TSK3_PRIO            4         //任务3 优先级
#define TASK3_STK_SIZE       (1*512)   //任务3 分配的堆栈大小


//开始任务
k_task_handle_t start_task_handle;      //开始任务 句柄
#define START_TSK_PRIO       1          //开始任务 优先级
#define START_TSK_STK_SIZE   1024       //开始任务 分配的堆栈大小


#define TEST_TIME_QUANTA 100
k_mutex_handle_t g_Mutex;   //互斥量


void task1(void)
{
    uint32_t i = 0;
    while(1)
    {
            csi_kernel_mutex_lock(g_Mutex,portMAX_DELAY);
            my_printf("Low task running!\r\n");
            for(i=0;i<2000000;i++)
            {
              taskYIELD();                        
            }                
            csi_kernel_mutex_unlock(g_Mutex);
            csi_kernel_delay_ms(1000);
    }
}


void task2(void)
{
    while(1)
    {
            my_printf("Middle task running!\r\n");
            csi_kernel_delay_ms(1000);
    }
}

void task3(void)
{
    while(1)
    {
            csi_kernel_delay_ms(500);
            my_printf("High task Pend Semaphore\r\n");
            csi_kernel_mutex_lock(g_Mutex, portMAX_DELAY);
            my_printf("High task running!\r\n");
            csi_kernel_mutex_unlock(g_Mutex);
            csi_kernel_delay_ms(500);
    }
}



void start_task(void)
{
     //进入临界区
    taskENTER_CRITICAL(); 
    
     //创建互斥量
    g_Mutex = csi_kernel_mutex_new();
    if(g_Mutex == NULL)
    {
        printf("fail to create mutex.\n");
    }

    //创建task 1
    csi_kernel_task_new((k_task_entry_t)task1, "task1", NULL, TSK1_PRIO, TEST_TIME_QUANTA, NULL, TASK1_STK_SIZE, &task1_handle);
    if (task1_handle == NULL) 
    {
        csi_kernel_sched_resume(0);
        my_printf("Fail to create task 1!\r\n");
    }

    //创建task 2
    csi_kernel_task_new((k_task_entry_t)task2, "task2", NULL, TSK2_PRIO, TEST_TIME_QUANTA, NULL, TASK2_STK_SIZE, &task2_handle);
    if (task2_handle == NULL) 
    {
        csi_kernel_sched_resume(0);
        my_printf("Fail to create task 2!\r\n");
    }
         
    //创建task 3
    csi_kernel_task_new((k_task_entry_t)task3, "task3", NULL, TSK3_PRIO, TEST_TIME_QUANTA, NULL, TASK3_STK_SIZE, &task3_handle);
    if (task3_handle == NULL) 
    {
        csi_kernel_sched_resume(0);
        my_printf("Fail to create task 3!\r\n");
    }
      

    //删除开始任务
    if(0 != csi_kernel_task_del(csi_kernel_task_get_cur()))
    {
            my_printf("Fail to delete start_task!\r\n");
    }
    else 
    {
            my_printf("start_task is deleted!\r\n");
    }

    
    //退出临界区
    taskEXIT_CRITICAL();            
}



void freertos_demo(void)
{

    my_printf("\r\n-->this is freertos task test demo!!!\r\n");                //print message
   
    //系统初始化
    csi_kernel_init();

        //创建开始任务
    csi_kernel_task_new((k_task_entry_t)start_task, "start_task", 0, START_TSK_PRIO, 0, 0, START_TSK_STK_SIZE, &start_task_handle);

        //任务调度开始
    csi_kernel_start();
        
}

4.3、实验现象

  • Task 3(High task)很快就得到运行的机会,不会被Task 2(Middle task)耽误很长时间。

### FreeRTOS互斥信号量的使用方法 #### 创建互斥信号量 为了创建一个互斥信号量,可以使用 `xQueueCreateMutex()` 函数。如果希望静态分配内存,则可采用 `xSemaphoreCreateMutexStatic()` 宏来实现[^3]。 ```c // 动态创建互斥信号量 SemaphoreHandle_t xMutex; xMutex = xQueueCreateMutex( queueQUEUE_TYPE_MUTEX ); if (xMutex != NULL) { // 互斥信号量创建成功 } else { // 处理错误情况 } ``` 对于静态创建的情况: ```c static StaticSemaphore_t xMutexBuffer; // 静态缓冲区 SemaphoreHandle_t xMutex; xMutex = xSemaphoreCreateMutexStatic(&xMutexBuffer); ``` #### 获取互斥信号量 当任务需要访问受保护的共享资源时,应先尝试获取互斥信号量。可以通过调用 `xSemaphoreTake()` 来完成这一操作。此函数会指定等待时间,在这段时间内如果没有获得锁将会超时并返回失败状态;也可以设置无限期等待直至得到锁为止[^2]。 ```c if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) { // 成功获取到互斥信号量,现在可以安全地访问临界区内的数据结构或其他资源 // 访问完成后记得释放互斥信号量 xSemaphoreGive(xMutex); } ``` #### 释放互斥信号量 一旦完成了对共享资源的操作,应当立即通过 `xSemaphoreGive()` 将互斥信号量归还给系统,以便其他正在等待的任务能够继续运行。 ```c if (xSemaphoreGive(xMutex) == pdPASS) { // 正常交出了互斥信号量 } ``` #### 错误处理与调试支持 在某些情况下可能还需要考虑如何捕获异常状况以及利用内置工具辅助开发过程中的问题排查工作。例如,`traceRETURN_xQueueCreateMutex()` 可帮助追踪互斥信号量创建的过程,这对于理解系统的实时行为非常有用[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值