10. 互斥量

10.1. 互斥量基本概念

互斥量又称互斥信号量(本质是信号量),是一种特殊的二值信号量,它和信号量不同的是, 它支持互斥量所有权、递归访问以及防止优先级翻转的特性,用于实现对临界资源的独占式处理。 任意时刻互斥量的状态只有两种,开锁或闭锁。当互斥量被任务持有时,该互斥量处于闭锁状态,这个任务获得互斥量的所有权。 当该任务释放这个互斥量时,该互斥量处于开锁状态,任务失去该互斥量的所有权。当一个任务持有互斥量时, 其他任务将不能再对该互斥量进行开锁或持有。持有该互斥量的任务也能够再次获得这个锁而不被挂起,这就是递归访问, 也就是递归互斥量的特性,这个特性与一般的信号量有很大的不同,在信号量中,由于已经不存在可用的信号量, 任务递归获取信号量时会发生主动挂起任务最终形成死锁。

如果想要用于实现同步(任务之间或者任务与中断之间),二值信号量或许是更好的选择, 虽然互斥量也可以用于任务与任务、任务与中断的同步,但是互斥量更多的是用于保护资源的互锁。

用于互锁的互斥量可以充当保护资源的令牌,当一个任务希望访问某个资源时,它必须先获取令牌。 当任务使用完资源后,必须还回令牌,以便其他任务可以访问该资源。是不是很熟悉,在我们的二值信号量里面也是一样的, 用于保护临界资源,保证多任务的访问井然有序。当任务获取到信号量的时候才能开始使用被保护的资源, 使用完就释放信号量,下一个任务才能获取到信号量从而可用使用被保护的资源。但是信号量会导致的另一个潜在问题, 那就是任务优先级翻转(具体会在下文讲解)。而FreeRTOS提供的互斥量可以通过优先级继承算法, 可以降低优先级翻转问题产生的影响,所以,用于临界资源的保护一般建议使用互斥量。

10.2. 互斥量的优先级继承机制

在FreeRTOS操作系统中为了降低优先级翻转问题利用了优先级继承算法。优先级继承算法是指, 暂时提高某个占有某种资源的低优先级任务的优先级,使之与在所有等待该资源的任务中优先级最高那个任务的优先级相等, 而当这个低优先级任务执行完毕释放该资源时,优先级重新回到初始设定值。 因此,继承优先级的任务避免了系统资源被任何中间优先级的任务抢占。

互斥量与二值信号量最大的不同是:互斥量具有优先级继承机制,而信号量没有。也就是说, 某个临界资源受到一个互斥量保护,如果这个资源正在被一个低优先级任务使用,那么此时的互斥量是闭锁状态, 也代表了没有任务能申请到这个互斥量,如果此时一个高优先级任务想要对这个资源进行访问,去申请这个互斥量, 那么高优先级任务会因为申请不到互斥量而进入阻塞态, 那么系统会将现在持有该互斥量的任务的优先级临时提升到与高优先级任务的优先级相同, 这个优先级提升的过程叫做优先级继承。这个优先级继承机制确保高优先级任务进入阻塞状态的时间尽可能短, 以及将已经出现的“优先级翻转”危害降低到最小。

没有理解?没关系,结合过程示意图再说一遍。我们知道任务的优先级在创建的时候就已经是设置好的, 高优先级的任务可以打断低优先级的任务,抢占CPU的使用权。但是在很多场合中,某些资源只有一个, 当低优先级任务正在占用该资源的时候,即便高优先级任务也只能乖乖的等待低优先级任务使用完该资源后释放资源。 这里高优先级任务无法运行而低优先级任务可以运行的现象称为“优先级翻转”。

为什么说优先级翻转在操作系统中是危害很大?因为在我们一开始创造这个系统的时候, 我们就已经设置好了任务的优先级了,越重要的任务优先级越高。但是发生优先级翻转,对我们操作系统是致命的危害, 会导致系统的高优先级任务阻塞时间过长。

举个例子,现在有3个任务分别为H任务(High)、M任务(Middle)、L任务(Low), 3个任务的优先级顺序为H任务>M任务>L任务。正常运行的时候H任务可以打断M任务与L任务,M任务可以打断L任务, 假设系统中有一个资源被保护了,此时该资源被L任务正在使用中,某一刻,H任务需要使用该资源, 但是L任务还没使用完,H任务则因为申请不到资源而进入阻塞态,L任务继续使用该资源,此时已经出现了“优先级翻转”现象, 高优先级任务在等着低优先级的任务执行,如果在L任务执行的时候刚好M任务被唤醒了,由于M任务优先级比L任务优先级高, 那么会打断L任务,抢占了CPU的使用权,直到M任务执行完,再把CPU使用权归还给L任务,L任务继续执行, 等到执行完毕之后释放该资源,H任务此时才从阻塞态解除,使用该资源。这个过程,本来是最高优先级的H任务, 在等待了更低优先级的L任务与M任务,其阻塞的时间是M任务运行时间+L任务运行时间,这只是只有3个任务的系统, 假如很多个这样子的任务打断最低优先级的任务,那这个系统最高优先级任务岂不是崩溃了,这个现象是绝对不允许出现的, 高优先级的任务必须能及时响应。所以,没有优先级继承的情况下,使用资源保护,其危害极大,具体见下图。

优先级翻转图解

图1优先级翻转图解

图1(1):L任务正在使用某临界资源, H任务被唤醒,执行H任务。但L任务并未执行完毕,此时临界资源还未释放。

图1(2):这个时刻H任务也要对该临界资源进行访问,但 L任务还未释放资源,由于保护机制,H任务进入阻塞态, L任务得以继续运行,此时已经发生了优先级翻转现象。

图1(3):某个时刻M任务被唤醒,由于M任务的优先级高于L任务, M任务抢占了CPU的使用权,M任务开始运行, 此时L任务尚未执行完,临界资源还没被释放。

图1(4):M任务运行结束,归还CPU使用权,L任务继续运行。

图1(5):L任务运行结束,释放临界资源,H任务得以对资源进行访问,H任务开始运行。

在这过程中,H任务的等待时间过长,这对系统来说这是很致命的,所以这种情况不允许出现, 而互斥量就是用来降低优先级翻转的产生的危害。

假如有优先级继承呢?那么,在H任务申请该资源的时候,由于申请不到资源会进入阻塞态, 那么系统就会把当前正在使用资源的L任务的优先级临时提高到与H任务优先级相同,此时M任务被唤醒了, 因为它的优先级比H任务低,所以无法打断L任务,因为此时L任务的优先级被临时提升到H,所以当L任务使用完该资源了, 进行释放,那么此时H任务优先级最高,将接着抢占CPU的使用权, H任务的阻塞时间仅仅是L任务的执行时间, 此时的优先级的危害降到了最低,看!这就是优先级继承的优势,具体如下。

优先级继承

图2优先级继承

图2(1):L任务正在使用某临界资源,L任务正在使用某临界资源, H任务被唤醒,执行H任务。 但L任务并未执行完毕,此时临界资源还未释放。

图2(2):某一时刻H任务也要对该资源进行访问,由于保护机制,H任务进入阻塞态。 此时发生优先级继承,系统将L任务的优先级暂时提升到与H任务优先级相同,L任务继续执行。

图2(3):在某一时刻M任务被唤醒,由于此时M任务的优先级暂时低于L任务,所以M任务仅在就绪态,而无法获得CPU使用权。

图2(4):L任务运行完毕,H任务获得对资源的访问权,H任务从阻塞态变成运行态,此时L任务的优先级会变回原来的优先级。

图2(5):当H任务运行完毕,M任务得到CPU使用权,开始执行。

图2(6):系统正常运行,按照设定好的优先级运行。

但是使用互斥量的时候一定需要注意:在获得互斥量后,请尽快释放互斥量,同时需要注意的是在任务持有互斥量的这段时间, 不得更改任务的优先级。FreeRTOS的优先级继承机制不能解决优先级反转,只能将这种情况的影响降低到最小, 硬实时系统在一开始设计时就要避免优先级反转发生。

10.3. 互斥量应用场景

互斥量的使用比较单一,因为它是信号量的一种,并且它是以锁的形式存在。在初始化的时候,互斥量处于开锁的状态, 而被任务持有的时候则立刻转为闭锁的状态。互斥量更适合于:

  • 可能会引起优先级翻转的情况。

递归互斥量更适用于:

  • 任务可能会多次获取互斥量的情况下。这样可以避免同一任务多次递归持有而造成死锁的问题。

多任务环境下往往存在多个任务竞争同一临界资源的应用场景,互斥量可被用于对临界资源的保护从而实现独占式访问。 另外,互斥量可以降低信号量存在的优先级翻转问题带来的影响。

比如有两个任务需要对串口进行发送数据,其硬件资源只有一个,那么两个任务肯定不能同时发送啦, 不然导致数据错误,那么,就可以用互斥量对串口资源进行保护,当一个任务正在使用串口的时候, 另一个任务则无法使用串口,等到任务使用串口完毕之后,另外一个任务才能获得串口的使用权。

另外需要注意的是互斥量不能在中断服务函数中使用,因为其特有的优先级继承机制只在任务起作用,在中断的上下文环境毫无意义。

10.4. 互斥量运作机制

多任务环境下会存在多个任务访问同一临界资源的场景,该资源会被任务独占处理。 其他任务在资源被占用的情况下不允许对该临界资源进行访问,这个时候就需要用到FreeRTOS的互斥量来进行资源保护, 那么互斥量是怎样来避免这种冲突?

用互斥量处理不同任务对临界资源的同步访问时,任务想要获得互斥量才能进行资源访问, 如果一旦有任务成功获得了互斥量,则互斥量立即变为闭锁状态,此时其他任务会因为获取不到互斥量而不能访问这个资源, 任务会根据用户自定义的等待时间进行等待,直到互斥量被持有的任务释放后,其他任务才能获取互斥量从而得以访问该临界资源, 此时互斥量再次上锁,如此一来就可以确保每个时刻只有一个任务正在访问这个临界资源,保证了临界资源操作的安全性。

互斥量运作机制

图3互斥量运作机制

图3(1):因为互斥量具有优先级继承机制,一般选择使用互斥量对资源进行保护, 如果资源被占用的时候,无论是什么优先级的任务想要使用该资源都会被阻塞。

图3(2):假如正在使用该资源的任务1比阻塞中的任务2的优先级还低, 那么任务1将被系统临时提升到与高优先级任务2相等的优先级(任务1的优先级从L 变成H)。

图3(3):当任务1使用完资源之后,释放互斥量,此时任务1的优先级会从H变回原来的L。

图3(4)-(5):任务2此时可以获得互斥量,然后进行资源的访问,当任务2访问了资源的时候, 该互斥量的状态又为闭锁状态,其他任务无法获取互斥量。

10.5. 互斥量控制块

cmsis_os2.h
1
2
3
4
5
6
typedef struct {
   const char                   *name;   //互斥量
   uint32_t                 attr_bits;   //创建的互斥量类型
   void                      *cb_mem;    //互斥量控制块(静态创建)内存
   uint32_t                   cb_size;   //互斥量控制块(静态创建)大小
} osMutexAttr_t;

其中cb_mem、cb_size成员变量用于信号量的静态创建, 当这两个成员变量为空时将使用动态内存分配的方式创建信号量。

在cmsis_os2.h中attr_bits成员变量的选项有以下三种, 实际上只有osMutexRecursive一种, FreeRTOS的互斥量默认已经支持优先级继承, 并不支持osMutexRobust参数。

cmsis_os2.h
1
2
3
#define osMutexRecursive      0x00000001U //递归互斥量
#define osMutexPrioInherit    0x00000002U //优先级继承
#define osMutexRobust         0x00000008U //当线程终止时,自动释放互斥锁

10.6. 互斥量函数接口讲解

10.6.1. 互斥量创建函数osMutexNew()

osMutexNew()用于创建一个事件组,并返回对应的事件组ID。 其函数源码如下

cmsis_os2.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
osMutexId_t osMutexNew (const osMutexAttr_t *attr) {
   SemaphoreHandle_t hMutex;
   uint32_t type;
   uint32_t rmtx;
   int32_t  mem;

#if (configQUEUE_REGISTRY_SIZE > 0)
   const char *name;
#endif

   hMutex = NULL;

   //不能在中断中使用osMutexNew函数
   if (!IS_IRQ()) {
      if (attr != NULL) {
         type = attr->attr_bits;
      } else {
         type = 0U;
      }

      //是否使用递归互斥锁
      if ((type & osMutexRecursive) == osMutexRecursive) {
         rmtx = 1U;
      } else {
         rmtx = 0U;
      }

      //FreeRTOS互斥锁不支持osMutexRobust
      if ((type & osMutexRobust) != osMutexRobust) {
         mem = -1;

         if (attr != NULL) {
            if ((attr->cb_mem != NULL) && (attr->cb_size >= sizeof(StaticSemaphore_t))) {
               mem = 1;              //静态内存创建互斥锁标志
            }
            else {
               if ((attr->cb_mem == NULL) && (attr->cb_size == 0U)) {
               mem = 0;    //动态内存创建互斥锁标志
               }
            }
         }
         else {
            mem = 0;
         }

         //静态创建互斥锁
         if (mem == 1) {
         //使用递归互斥锁
            if (rmtx != 0U) {
               hMutex = xSemaphoreCreateRecursiveMutexStatic (attr->cb_mem);
            }
         //不使用递归互斥锁
            else {
               hMutex = xSemaphoreCreateMutexStatic (attr->cb_mem);
            }
         }
         //动态创建互斥锁
         else {
            if (mem == 0) {
               //使用递归互斥锁
               if (rmtx != 0U) {
                  hMutex = xSemaphoreCreateRecursiveMutex ();
               //不使用递归互斥锁
               } else {
                  hMutex = xSemaphoreCreateMutex ();
               }
            }
         }

   #if (configQUEUE_REGISTRY_SIZE > 0)
         if (hMutex != NULL) {
            if (attr != NULL) {
               name = attr->name;
            } else {
               name = NULL;
            }
            vQueueAddToRegistry (hMutex, name);
         }
   #endif

         //如果使用了递归互斥量,将得到的句柄(实际是指针)最后一位设置为1
         //32位的地址一般都是以4字节对齐,低两位为00
         if ((hMutex != NULL) && (rmtx != 0U)) {
            hMutex = (SemaphoreHandle_t)((uint32_t)hMutex | 1U);
         }
      }
   }

   return ((osMutexId_t)hMutex);
}

参数

  • attr :描述互斥量属性的结构体

返回值: 若创建成功则返回事件组ID,用于访问创建的事件组。创建失败则返回NULL

10.6.2. 互斥量删除函数osMutexDelete()

互斥量的本质也是信号量的一种,在osMutexDelete函数中删除互斥量也就是删除信号量,其函数源码如下

cmsis_os2.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
osStatus_t osMutexDelete (osMutexId_t mutex_id) {
   osStatus_t stat;
#ifndef USE_FreeRTOS_HEAP_1
   SemaphoreHandle_t hMutex;

   hMutex = (SemaphoreHandle_t)((uint32_t)mutex_id & ~1U);  //获取互斥量句柄

   //不能在中断里删除互斥锁
   if (IS_IRQ()) {
      stat = osErrorISR;
   }
   //参数出错判断
   else if (hMutex == NULL) {
      stat = osErrorParameter;
   }
   else {
   #if (configQUEUE_REGISTRY_SIZE > 0)
      vQueueUnregisterQueue (hMutex);
   #endif
      stat = osOK;
      vSemaphoreDelete (hMutex);
   }
#else
   stat = osError;
#endif

   return (stat);
}

参数

  • ef_id :互斥量ID

返回值:当成功时返回osOK,失败返回负值。

10.6.3. 互斥量获取函数osMutexAcquire()

我们知道,当互斥量处于开锁的状态,任务才能获取互斥量成功,当任务持有了某个互斥量的时候, 其它任务就无法获取这个互斥量,需要等到持有互斥量的任务进行释放后,其他任务才能获取成功, 任务通过互斥量获取函数来获取互斥量的所有权。任务对互斥量的所有权是独占的,任意时刻互斥量只能被一个任务持有, 如果互斥量处于开锁状态,那么获取该互斥量的任务将成功获得该互斥量,并拥有互斥量的使用权;如果互斥量处于闭锁状态, 获取该互斥量的任务将无法获得互斥量,任务将被挂起,在任务被挂起之前,会进行优先级继承, 如果当前任务优先级比持有互斥量的任务优先级高,那么将会临时提升持有互斥量任务的优先级。其互斥量获取函数源码如下

cmsis_os2.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
osStatus_t osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout) {
   SemaphoreHandle_t hMutex;
   osStatus_t stat;
   uint32_t rmtx;

   hMutex = (SemaphoreHandle_t)((uint32_t)mutex_id & ~1U);   //获取句柄

   rmtx = (uint32_t)mutex_id & 1U;                                                   //递归互斥量判断

   stat = osOK;

   //不能在中断里使用osMutexAcquire函数
   if (IS_IRQ()) {
      stat = osErrorISR;
   }
   //参数出错判断
   else if (hMutex == NULL) {
      stat = osErrorParameter;
   }

   else {
   //使用递归互斥量
      if (rmtx != 0U) {
         if (xSemaphoreTakeRecursive (hMutex, timeout) != pdPASS) {
            if (timeout != 0U) {
               stat = osErrorTimeout;
            } else {
               stat = osErrorResource;
            }
         }
      }
   //不使用递归互斥量
      else {
         if (xSemaphoreTake (hMutex, timeout) != pdPASS) {
            if (timeout != 0U) {
               stat = osErrorTimeout;
            } else {
               stat = osErrorResource;
            }
         }
      }
   }

   return (stat);
}

参数

  • mutex_id :互斥量ID

  • timeout :线程超时等待时间

返回值:当成功时返回osOK,失败返回负值。

10.6.4. 互斥量释放函数osMutexRelease()

任务想要访问某个资源的时候,需要先获取互斥量,然后进行资源访问,在任务使用完该资源的时候, 必须要及时归还互斥量,这样别的任务才能对资源进行访问。在前面的讲解中,我们知道,当互斥量有效的时候, 任务才能获取互斥量,那么,是什么函数使得信号量变得有效呢? CMSIS-RTOS给我们提供了互斥量释放函数osMutexRelease(),任务可以调用osMutexRelease()函数进行释放互斥量, 表示我已经用完了,别人可以申请使用,互斥量的释放只能在任务中,不允许在中断中释放互斥量。 使用该函数接口时,只有已持有互斥量所有权的任务才能释放它,此时会将互斥量变为开锁状态, 等待获取该互斥量的任务将被唤醒。如果任务的优先级被互斥量的优先级翻转机制临时提升, 那么当互斥量被释放后,任务的优先级将恢复为原本设定的优先级,其函数源码如下所示

cmsis_os2.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
osStatus_t osMutexRelease (osMutexId_t mutex_id) {
   SemaphoreHandle_t hMutex;
   osStatus_t stat;
   uint32_t rmtx;

   hMutex = (SemaphoreHandle_t)((uint32_t)mutex_id & ~1U); //获取句柄

   rmtx = (uint32_t)mutex_id & 1U;                                             //递归互斥量锁判断

   stat = osOK;

   //不能在中断里使用释放互斥锁函数osMutexRelease
   if (IS_IRQ()) {
      stat = osErrorISR;
   }
   //参数出错判断
   else if (hMutex == NULL) {
      stat = osErrorParameter;
   }

   else {
      //使用递归互斥量
      if (rmtx != 0U) {
         if (xSemaphoreGiveRecursive (hMutex) != pdPASS) {
            stat = osErrorResource;
         }
      }
      //不使用递归互斥量
      else {
         if (xSemaphoreGive (hMutex) != pdPASS) {
            stat = osErrorResource;
         }
      }
   }

   return (stat);
}
  • mutex_id :互斥量ID

返回值:当成功时返回osOK,失败返回负值。

10.7. 实验

10.7.1. 模拟优先级翻转实验

模拟优先级翻转实验是在 FreeRTOS 中创建了三个任务与一个二值信号量,任务分别是高优先级任务, 中优先级任务,低优先级任务,用于模拟产生优先级翻转。低优先级任务在获取信号量的时候,被中优先级打断, 中优先级的任务执行时间较长,因为低优先级还未释放信号量,那么高优先级任务就无法取得信号量继续运行, 高优先级任务等待比自己优先级低的任务,此时就发生了优先级翻转,任务在运行中,使用串口打印出相关信息,

关于二值信号量以及线程的创建之前的章节中已经介绍过了,这里只做简单介绍。 本小节只给出重点部分代码,完整代码请打开工程查看。

二值信号量配置如下

二值信号量配置

我们需要配置三个优先级不同的线程,其线程配置如下

线程配置

10.7.2. app_freertos.c

10.7.2.1. LowPriority_Task线程

app_freertos.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void LowPriority_Task(void *argument)
{
   static uint32_t i;
   osStatus_t xReturn;
   for(;;)
   {
      printf("LowPriority_Task 正在获取互斥量......\n\n");
      //获取二值信号量 xSemaphore,没获取到则一直等待
      xReturn = osSemaphoreAcquire (BinarySem01Handle, osWaitForever);
      if( xReturn == osOK )
         printf("LowPriority_Task Running\n\n");

      for(i=0;i<2000000;i++)//模拟低优先级任务占用信号量
      {
         osThreadYield();//发起任务调度
      }

      printf("LowPriority_Task 释放信号量!\r\n");
      xReturn = osSemaphoreRelease( BinarySem01Handle );//给出二值信号量
      osDelay(500);
   }
}

10.7.2.2. MidPriority_Task线程

app_freertos.c
1
2
3
4
5
6
7
8
void MidPriority_Task(void *argument)
{
   for(;;)
   {
      printf("MidPriority_Task Running\n");
      osDelay(500);
   }
}

10.7.2.3. HighPriority_Task线程

app_freertos.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void HighPriority_Task(void *argument)
{
   osStatus_t xReturn;
   for(;;)
   {
      printf("HighPriority_Task 正在获取互斥量......\n\n");
      //获取二值信号量 xSemaphore,没获取到则一直等待
      xReturn = osSemaphoreAcquire (BinarySem01Handle, osWaitForever);

      if(osOK == xReturn)
         printf("HighPriority_Task Running\n");

      xReturn = osSemaphoreRelease( BinarySem01Handle );//给出二值信号量
      if( osOK == xReturn )
         printf("HighPriority_Task 释放信号量!\r\n");

      osDelay(500);
   }
}

10.7.3. 模拟优先级翻转实验现象

优先级翻转

10.7.4. 互斥量实验

互斥量实验是基于优先级翻转实验进行修改的,使用互斥量代替二值信号量,以测试互斥量的优先级继承 机制是否有效。

互斥量在STM32CubeIDE的配置选项如下

互斥量

互斥量和二值信号量的配置基本上一模一样,互斥量在二值信号量的基础上添加了优先级继承。 线程配置和优先级翻转实验一模一样。直接看下线程实现相关代码。

10.7.4.1. LowPriority_Task线程

app_freertos.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void LowPriority_Task(void *argument)
{

   static uint32_t i;
   osStatus_t ret;

   for(;;)
   {
      printf("LowPriority_Task 正在获取互斥量......\n");
      ret = osMutexAcquire (Mutex01Handle, osWaitForever);
      if( ret == osOK)
      {
         printf("LowPriority_Task Running\n");
      }

      for(i=0;i<2000000;i++)//模拟低优先级任务占用互斥量
      {
         osThreadYield();//发起任务调度
      }

      printf("LowPriority_Task 释放互斥量\r\n");
      ret = osMutexRelease(Mutex01Handle);

      osDelay(1000);
   }
/* USER CODE END LowPriority_Task */
}

10.7.4.2. MidPriority_Task线程

app_freertos.c
1
2
3
4
5
6
7
8
void MidPriority_Task(void *argument)
{
   for(;;)
   {
      printf("MidPriority_Task Running\n");
      osDelay(1000);
   }
}

10.7.4.3. HighPriority_Task线程

app_freertos.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void HighPriority_Task(void *argument)
{

   osStatus_t ret;

   for(;;)
   {
      //获取互斥量
      printf("HighPriority_Task 正在获取互斥量.......\n");
      ret = osMutexAcquire (Mutex01Handle, osWaitForever);
      if( ret == osOK)
      {
         printf("HighPriority_Task Running\n");
      }

      //释放信号量
      ret = osMutexRelease(Mutex01Handle);
      if( ret == osOK)
      {
         printf("HighPriority_Task 释放互斥量!\r\n");
      }
      osDelay(1000);
   }
}

10.7.5. 互斥量实验现象

在串口助手中我们可以看到在低优先级任务运行的时候,中优先级任务无法抢占低优先级的任务, 这是因为互斥量的优先级继承机制,从而最大程度降低了优先级翻转产生的危害。

互斥量实验