|
第15章 互斥信号量 本章节开始讲解RTX的另一个重要的资源共享机制---互斥信号量(Mutex,即Mutual Exclusion的缩写)。注意,建议初学者学习完上个章节的信号量后再学习本章节的互斥信号量。 本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407。 15.1 互斥信号量 15.2 互斥信号量API函数 15.3 实验例程说明 15.4 总结 15.1 互斥信号量) B7 [0 p- r5 J) P' D- m' x. q
15.1.1 互斥信号量的概念及其作用 互斥信号量就是信号量的一种特殊形式,也就是信号量初始值为1的情况。有些RTOS中也将信号量初始值设置为1的情况称之为二值信号量。为什么叫二值信号量呢?因为信号量资源被获取了,信号量值就是0,信号量资源被释放,信号量值就是1,把这种只有0和1两种情况的信号量称之为二值信号量。互斥信号量的主要作用就是对资源实现互斥访问。下面举一个通过二值信号量实现资源独享,即互斥访问的例子,让大家有一个形象的认识 运行条件: 1. 让两个任务Task1和Task2都有运行串口打印printf,这里我们就对函数printf通过二值信号量实现互斥访问。如果不对函数printf进行互斥访问,串口打印容易出现乱码。 2. 用信号量实现二值信号量只需将信号量的初始值设置为1即可。 代码实现: 创建二值信号量 - 7 F {% O8 n. y4 W
- OS_SEM semaphore;
1 Z* G) F2 `8 \6 `, {' P - 8 ^$ f8 p; b5 U$ A% ?
- /** q/ G7 w+ U* s: Z/ D5 {8 l
- *********************************************************************************************************3 L1 c2 U, K" l5 ]5 f. P
- * 函 数 名: AppObjCreate" K2 }$ i; F# K {
- * 功能说明: 创建任务通信机制+ B1 ]/ N3 Z! k) r/ j. x
- * 形 参: 无: ]$ k$ @" N# D) J3 d
- * 返 回 值: 无
& G1 C/ g; h. a0 n2 B - *********************************************************************************************************2 i, J/ d; P1 J
- */
4 _: `& e% O/ ~; L2 y7 |* [ - static void AppObjCreate (void)6 L6 A( y8 K- N5 @# d
- {
, O$ l: I( d# f1 Z3 B - /* 创建二值信号量,实现对互斥资源的独享 */& f5 ^- X+ m5 x8 B
- os_sem_init (semaphore, 0);
) R# b( y V) P5 s5 G. H- d6 O - }
复制代码
; c) F, O! Q, y' A6 ~/ l通过二值信号量实现对printf函数互斥访问的两个任务 - /*
/ j6 }" K0 p% D/ b - *********************************************************************************************************; K2 o5 U5 N& t7 j+ O% |5 ?# @
- * 函 数 名: AppTaskLED& L1 \8 r9 e5 r8 U, S1 N
- * 功能说明: 串口打印- o, e5 f; J; u0 D3 T% l6 @9 _
- * 形 参: 无
" p$ J" |* J' H - * 返 回 值: 无
, c$ O [- J2 \ - * 优 先 级: 2 : `. n6 d) E/ v# U
- *********************************************************************************************************
/ l3 l/ w1 ]( s; x# x% G2 v - */4 Z7 H& _% Z' e
- __task void AppTaskLED(void)
- `- _) b8 R8 b, F' G - {3 w4 V: N; W! w" m( ]1 q, H
- const uint16_t usFrequency = 1000; /* 延迟周期 */' Q% n( `8 G0 u8 Q
-
! {2 ]) D5 a* O; \ - /* 设置延迟周期 */
, x6 s- U% H# m# i* X! e9 g- d6 j - os_itv_set(usFrequency);
$ Z+ Z$ V' c4 f - ]: i- v" T% e1 p$ Q0 L
- while(1)* N' k! V/ v; u+ u% i" K
- {8 o7 o2 s9 `8 S1 `
- 3 M8 W0 P* r' I0 S6 _: G
- /* 永久等待互斥资源可用 */: r3 [, n9 I# }( e# j ~) W( ~
- os_sem_wait (&semaphore, 0xffff);" b7 J$ t0 m; A' g/ d: @3 L
- printf("任务AppTaskLED正在运行\r\n");
, e# ` w- {# c) h4 J' x/ } - /* 释放互斥资源 */2 l$ |% H+ X: D+ V; t2 T6 u
- os_sem_send (&semaphore);
+ J' B3 R3 |6 g+ `+ `* Z$ D8 c6 d& W - 5 g1 c0 z! q5 u: J- P0 a% v
- /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/! H4 I6 p. r; f9 M L7 G. \. q7 r5 T. w
- os_itv_wait();
: i/ n% K" |' s. }- x( p6 p1 t) m - }% c/ `$ k2 n7 W7 `* I' a
- }, A0 v% B" |$ d. r# A
-
5 t3 h$ j9 A9 ^/ q: K - /*/ M/ p7 _0 Y' e" a2 L
- *********************************************************************************************************
" z9 ^# }! E( o8 {. @: w. i2 V - * 函 数 名: AppTaskMsgPro
5 \$ { O P ^( ]4 r" | - * 功能说明: 串口打印* E# ?4 l. U' q6 ? [( e
- * 形 参: 无
" b2 o" A5 W/ F - * 返 回 值: 无
/ L' U- j: P M* l! L - * 优 先 级: 3 ! n G7 y) ?; ]
- *********************************************************************************************************! G/ X6 Y, L6 U* K( K
- */
% A" p8 \, }, W0 x - __task void AppTaskMsgPro(void)
# i" v# {2 b0 f/ \ - {% a1 Z- p( Z0 `2 U* Y! e ^
- const uint16_t usFrequency = 1000; /* 延迟周期 */
8 q% H8 ^, A) {+ K - 5 W# w. W, M* C. u4 b6 H; j1 d
- /* 设置延迟周期 */
, q4 t# }# \0 E9 ^$ u+ v( q+ s - os_itv_set(usFrequency);
: T& K* Y& W" z; U* K - G0 R& T) h! ~7 j' W1 M4 X7 Z, z
- while(1)7 w9 H* q" k2 ]# Z/ V. T, {: Z6 a( ~
- {
7 h- B; B: J$ \1 P) K7 H# L( F - /* 永久等待互斥资源可以 */0 {: h* Y$ p" ^7 b7 N- K4 w8 g
- os_sem_wait (&semaphore, 0xffff);
. T5 |& H4 `$ N6 x) I0 v - printf("任务AppTaskMsgPro正在运行\r\n");
2 b- X# k4 N( t% ^: }8 u - /* 释放互斥资源 */2 A% o! _, f$ _9 R, Q1 F
- os_sem_send (&semaphore);& j! P2 T* i! G+ `7 d
-
4 m2 z y) K% I% G - /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
& s& v' l% B% C _& d+ R/ k - os_itv_wait();
k) e5 m/ l4 S0 t! F - }4 V" F4 G/ }5 o* E8 K! u) J
- }
复制代码 - n% u( S- F! L/ Y
有了上面二值信号量的认识之后,互斥信号量跟二值信号量又有什么区别呢?互斥信号量可以防止优先级翻转,而二值信号量不支持,下面我们就讲解一下优先级翻转问题。
2 b7 g2 |7 z; {* r15.1.2 优先级翻转问题 下面我们通过如下的框图来说明一下优先级翻转的问题,让大家有一个形象的认识。 运行条件: 1. 创建3个任务Task1,Task2和Task3,优先级分别为3,2,1。也就是Task1的优先级最高 2. 任务Task1和Task3互斥访问串口打印printf,采用二值信号实现互斥访问。 3. 起初Task3通过二值信号量正在调用printf,被任务Task1抢占,开始执行任务Task1,也就是上图的起始位置。 运行过程描述如下: 1. 任务Task1运行的过程需要调用函数printf,发现任务Task3正在调用,任务Task1会被挂起,等待Task3释放函数printf。 2. 在调度器的作用下,任务Task3得到运行,Task3运行的过程中,由于任务Task2就绪,抢占了Task3的运行。优先级翻转问题就出在这里了,从任务执行的现象上看,任务Task1需要等待Task2执行完毕才有机会得到执行,这个与抢占式调度正好反了,正常情况下应该是高优先级任务抢占低优先级任务的执行,这里成了高优先级任务Task1等待低优先级任务Task2完成。所以这种情况被称之为优先级翻转问题。 3. 任务Task2执行完毕后,任务Task3恢复执行,Task3释放互斥资源后,任务Task1得到互斥资源,从而可以继续执行。 2 x$ d/ {! o' e5 c- _
上面就是一个产生优先级翻转问题的现象。
3 U4 W' R' `. X5 X2 Y9 g- W' ]15.1.3 RTX互斥信号量的实现 RTX互斥信号量是怎么实现的呢?其实相比二值信号量就是解决了一下优先级翻转的问题。下面我们通过如下的框图来说明一下RTX互斥信号量的实现,让大家有一个形象的认识。 运行条件: 1. 创建2个任务Task1和Task2,优先级分别为1和3,也就是任务Task2的优先级最高 2. 任务Task1和Task2互斥访问串口打印printf。 3. 使用RTX的互斥信号量实现串口打印printf的互斥访问。 运行过程描述如下: 1. 低优先级任务Task1执行过程中先获得互斥资源printf的执行。此时任务Task2抢占了任务Task1的执行,任务Task1被挂起。任务Task2得到执行。 2. 任务Task2执行过程中也需要调用互斥资源,但是发现任务Task1正在访问,此时任务Task1的优先级会被提升到跟Task2同一个优先级,也就是优先级3,这个就是所谓的优先级继承(Priorityinheritance),这样就有效的防止了优先级翻转问题。任务Task2被挂起,任务Task1有新的优先级继续执行。 3. 任务Task1执行完毕并释放互斥资源后,优先级恢复到原来的水平。由于互斥资源可以使用,任务Task2获得互斥资源后开始执行。
7 |5 Z4 |, m$ Z
上面就是一个简单RTX互斥信号量的实现过程。 - j! t _* W6 `. P' K
15.1.4 RTX中断方式互斥信号量的实现 互斥信号量仅支持用在RTX的任务中,中断函数中不可使用。
: ]5 s) P: e& W5 \) `: k |
15.2.1 函数os_mut_init
15.2.2 函数os_mut_wait
15.2.3 函数os_mut_release
15.3.1 STM32F103开发板实验