FreeRTOS信号量_优先级翻转_互斥信号量

文章讲述了在使用二值信号量和计数型信号量时可能出现的优先级翻转问题,尤其是在实时操作系统中,这种问题可能导致任务执行顺序混乱。通过示例和互斥信号量的分析,阐述了如何理解和解决优先级翻转现象,以及在FreeRTOS中的应用和限制。
摘要由CSDN通过智能技术生成
优先级翻转       
       在使用二值信号量和计数型信号量的时候,经常会遇到优先级翻转的问题,优先级在抢占 式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转 会破坏任务的预期顺序,可能会导致未知的严重后果,下面展示了一个优先级翻转的例子,如下图所示:

       优先级翻转示意图,如上图所示,定义:任务 H 为优先级最高的任务,任务 L 为优先级中 最低的任务,任务 M 为优先级在任务 H 与任务 L 之间的任务。

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

(2) 此时任务 L 要访问共享资源,因此需要获取信号量。

(3) 任务 L 成功获取信号量,并且此时信号量已无资源,任务 L 开始访问共享资源。

(4) 此时任务 H 就绪,抢占任务 L 运行。

(5) 任务 H 开始运行。 

(6) 此时任务 H 要访问共享资源,因此需要获取信号量,但信号量已无资源,因此任务 H 阻塞等待信号量资源。 

(7) 任务 L 继续运行。 

(8) 此时任务 M 就绪,抢占任务 L 运行。 

(9) 任务 M 正在运行。 

(10) 任务 M 运行完毕,继续阻塞。 

(11) 任务 L 继续运行。 

(12) 此时任务 L 对共享资源的访问操作完成,释放信号量,虽有任务 H 因成功获取信号 量,解除阻塞并抢占任务 L 运行。 

(13) 任务 H 得以运行。 从上面优先级翻转的示例中,可以看出,任务 H 为最高优先级的任务,因此任务 H 执行的 操作需要有较高的实时性,但是由于优先级翻转的问题,导致了任务 H 需要等到任务 L 释放信 号量才能够运行,并且,任务 L 还会被其他介于任务 H 与任务 L 任务优先级之间的任务 M 抢 占,因此任务 H 还需等待任务 M 运行完毕,这显然不符合任务 H 需要的高实时性要求。

优先级程序实验示例
程序设计:分别定义三个不同优先级的任务:高优先级任务TASK_H;中优先级任务TASK_M;低优先级任务TASK_L。初始钥匙为一把,即信号量初始为1。
CountSemaphore = xSemaphoreCreateCounting( (UBaseType_t)1, (UBaseType_t)1);

#define TASK_L_PRIO             2
#define TASK_L_STK_SIZE         128
TaskHandle_t Task_L_Task_Handler;
void task_l_task(void *pvParameters);


#define TASK_M_PRIO             3
#define TASK_M_STK_SIZE         128
TaskHandle_t Task_M_Task_Handler;
void task_m_task(void *pvParameters);


#define TASK_H_PRIO             4
#define TASK_H_STK_SIZE         128
TaskHandle_t Task_H_Task_Handler;
void task_h_task(void *pvParameters);

void task_l_task(void *pvParameters)
{
    unsigned char task_l_num = 0;
    
    while(1)
    {
        printf("\r\nTask L ready to take semaphore\r\n");
        xSemaphoreTake(CountSemaphore, portMAX_DELAY);
        printf("\r\nTask L has taked semaphore\r\n");
        
        for(task_l_num = 0; task_l_num < 5; task_l_num++)
        {
             printf("\r\nTask L running\r\n");
             delay_xms(100);
        }
        
        printf("\r\nTask L give semaphore\r\n");
        xSemaphoreGive(CountSemaphore);
        
        vTaskDelay(1000);
    }
}


void task_m_task(void *pvParameters)
{
    unsigned char task_m_num = 0;
        
    vTaskDelay(200);
    
    while(1)
    {
         for(task_m_num = 0; task_m_num < 5; task_m_num++)
        {
             printf("\r\nTask M running\r\n");
             delay_xms(100);
        }
        
        vTaskDelay(1000);
    }
}


void task_h_task(void *pvParameters)
{
    
    vTaskDelay(500);
    while(1)
    {
        printf("\r\nTask H ready to take semaphore\r\n");
        xSemaphoreTake(CountSemaphore, portMAX_DELAY);
        printf("\r\nTask H has taked semaphore\r\n");
        printf("\r\nTask H running\r\n");
        printf("\r\nTask H give semaphore\r\n");
        xSemaphoreGive(CountSemaphore);
        
        vTaskDelay(100);
    }
}

下载验证

拓展验证:

当钥匙不止一把时,即信号量初始不是一个,而是两个时:

CountSemaphore = xSemaphoreCreateCounting( (UBaseType_t)3, (UBaseType_t)2);

FreeRTOS 互斥信号量
FreeRTOS 互斥信号量简介
        互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或 中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。 在互斥访问中互斥信号量相当于一把钥匙,当任务想要访问共享资源的时候就必须先获得这把 钥匙,当访问完共享资源以后就必须归还这把钥匙,这样其他的任务就可以拿着这把钥匙去访问资源。
        互斥信号量使用和二值信号量相同的 API 操作函数,所以互斥信号量也可以设置阻塞时间, 不同于二值信号量的是互斥信号量具有优先级继承的机制。当一个互斥信号量正在被一个低优先级的任务持有时,如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优 先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相 同的优先级,这个过程就是优先级继承。优先级继承尽可能的减少了高优先级任务处于阻塞态 的时间,并且将“优先级翻转”的影响降到最低。
       优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的 影响。实时应用应该在设计之初就要避免优先级翻转的发生。互斥信号量不能用于中断服务函 数中,原因如下:
      (1) 互斥信号量有任务优先级继承的机制,但是中断不是任务,没有任务优先级,所以互斥 信号量只能用与任务中,不能用于中断服务函数。
      (2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。
FreeRTOS 互斥信号量相关 API 函数
        FreeRTOS 提供了互斥信号量的一些相关操作函数,其中常用的互斥信号量相关 API 函数, 如下表所示:
      
       从上面中可以看出,互斥信号量除了创建函数之外,其余的获取、释放等信号量操作函数, 都与二值信号量相同,因此这里重点讲解互斥信号量的创建函数。

函数 xSemaphoreCreateMutex()

      此函数用于使用动态方式创建互斥信号量,创建互斥信号量所需的内存,由 FreeRTOS 从 FreeRTOS 管理的堆中进行分配。该函数实际上是一个宏定义,在 semphr.h 中有定义,具体的 代码如下所示:

      从 上 面 的 代 码 中 可 以 看 出 , 函 数 xSemaphoreCreateMutex() 实 际 上 是 调 用 了 函 数 xQueueCreateMutex(),函数 xQueueCreateMutex()在 queue.c 文件中有定义。

FreeRTOS 互斥信号量操作实验
程序设计:在上一个例程基础上,将计数型信号量改变为互斥型信号量。然后根据运行结果与上一个例程进行对比。
NOTE:使用互斥信号量需要在FreeRTOSConfig.h中进行配置:
#define configUSE_MUTEXES  1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
程序实例:
CountSemaphore = xSemaphoreCreateCounting( (UBaseType_t)1, (UBaseType_t)1);

CountSemaphore = xSemaphoreCreateMutex();

       以上两个函数分别切换注释,下载验证。下面左图为互斥信号量,右图为计数型信号量。可以发现:

        当一个互斥信号量正在被一个低优先级的任务持有时,如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优 先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

        所以和上一个例程不同的是,在高优先级任务开始尝试获取资源后,中优先级任务就不再运行了,原因就在于把占有钥匙的低优先级任务拉到了和高优先级任务一样的优先级。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

moon2shine

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值