FreeRTOS任务间通讯

队列 
二进制信号灯 
计数信号灯 
互斥 
递归互斥, 以及更多 


--------------------------------------------------------------------------------

队列
队列是内部通信的主要形式。它可以用于在任务和任务之间以及任务和中断之间发送消息。在大多数情况下使用线程安全 FIFO(先进先出)缓存,新数据放在队列的最后,虽然数据也可以放在前面。
队列可以包含固定大小的 '项目' - 每个项目的大小和队列可以保存项目的最大数量在创建队列时就已经定义了。 

项目以复制而不是引用的方式放入队列,因此最好使放入队列项目的大小成为最小。以复制的方式放入队列可以使你的系统设计极大的简化,因为两个任务不会同时访问数据。队列帮助你管理所有的互斥问题。

如果你希望在队列中使用大的项目,可能最好用插入队列指针 - 但是这样做必须注意要确保你的系统明确定义任务和/或中断是数据的"所以者"。

队列 API 函数可以指定阻塞的时间。阻塞时间代表任务进入阻塞状态或者等待队列中数据时(当任务读取队列但是队列是空的时候)的最大'节拍'数,或者等待队列空间变为可以使用(当任务需要写数据到队列,但是队列已满时)。当一个以上任务在同一个队列中被阻塞时,高优先级的任务先解除阻塞。

查看用户文档中 队列管理 小节中与队列相关的 API 函数列表。搜索 FreeRTOS/Demo/Common/Minimal 文件夹下的文件可以发现多个这种用法的例子。注意中断里不能使用不是用 "FromISR" 结束的 API 函数。



4.gif 
写入和读取队列。在这个例子中队列保存5个项目,并且从不变满。




--------------------------------------------------------------------------------

二进制信号灯
二进制信号灯同时用于互斥和同步的目的。
二进制信号灯和互斥非常相似,但是有一些细微的不同:互斥包括优先级继承机制,而二级制信号没有。这使得二进制信号灯用于同步更方便 (在任务之间或任务与中断之间),而互斥用在简单的互相排斥更好。在怎样用互斥作为互相排斥的机制中 说明 二进制信号灯用法,这个子章节只说明使用二进制信号灯进行同步。

信号灯 API 函数可以指定阻塞的时间。阻塞时间代表任务因为等待获取信号灯而进入阻塞状态的最大'节拍'数。如果超过一个以上的任务因为同一个信号灯被阻塞,那么在信号灯可以使用时高优先级的任务先解除阻塞。

将一个二进制信号灯看作为队列,它只能保存一个项目,这个队列只能是空的或者是满的 (二进制)。使用队列的任务和中断不关心队列保存了什么 - 它们只关系队列是空的还是满的。这个机制可以用于同步任务和中断。

考虑这样一个情况,任务使用一个外设,轮询外设将浪费 CPU 资源,并阻止了其他任务的运行。因此最好是任务将大部分时间用于阻塞状态 (允许其他任务运行),只有真正需要操作时才去轮询。这就是在 '试图' 获得信号时使用二进制信号灯进行任务阻塞,而一个中断服务程序用于外设,当需要使用外设时 '给出' 信号。任务总是 '获取' 信号 (从队列读取直到队列为空),但是从不 '给出'。中断服务程序总是 '给出' 信号 (写入队列直到队列变满) 而从不获取。 xSemaphoreGiveFromISR() 源代码的文档中清楚的解释了这个方法。

可以使用任务的优先级保证及时的外设服务 - 有效的产生 '不同的中断' 方式。另外一种方法是使用队列代替信号,在中断服务程序中捕捉外设的数据并通过队列发送给任务。当队列的数据可以使用后任务解除阻塞,从队列读取数据后,进行数据处理。第二种方法允许中断程序尽可能的短,通过 post 处理而不是发生在一个任务中。

参考用户文档的 信号灯/互斥 小节中关于信号灯的 API 函数。搜索 FreeRTOS/Demo/Common/Minimal 文件夹下的文件可以获得很多这种用法的例子。注意中断里不能使用不是以 "FromISR" 结束的 API 函数。



5.gif 
使用信号灯同步任务和中断。中断只 '给出' 信号,而任务只 '获取' 信号。




--------------------------------------------------------------------------------

计数信号灯
正如二进制信号灯可以认为长度是 1 的队列,计数信号灯可以看成是长度大于 1 的队列。同样,用户的信号不是对队列数据的数值感兴趣 - 只需要看队列是不是空的。
计数信号灯典型用于两个方面:

计数事件。
在这个情况下使用一个事件处理程序将在每次事件发生时 '给出' 信号 (增加信号计数值), 同时每次处理事件时处理任务将 '获得' 信号 (减少信号计数值)。因此计数值是事件发生次数和处理次数的差,信号创建时,这个计数值是0。


资源管理。
在这个情况下,计数值代表可用的资源数。为了获得一个资源的控制,任务必须先获得信号 - 减少信号的计数值。当计数值达到 0,代表没有剩余的自由资源。当任务使用完资源后,需要 '返还' 信号 - 增加信号计数值。在这种方式下信号创建时的计数值等于最大计数值。 

参考 信号灯/互斥 小节查看相关的 API 函数。搜索 FreeRTOS/Demo/Common/Minimal 文件夹下的文件可以查看关于这种用法的例子。注意中断里不能使用不以 "FromISR" 结束的 API 函数。



--------------------------------------------------------------------------------

互斥
互斥是包含优先级继承机制的 二进制信号灯。对于同步(在任务之间或者任务与中断之间)来说二进制信号灯是更好的选择,互斥对于简单的互相排斥更方便 (mutex 就是 'MUT'ual 'EX'clusion). 
当用于互相排斥时,互斥就像是资源保护。当一个任务需要访问资源,它必须先获得 ('take') 令牌;当访问结束后,它必须释放令牌 - 允许其他任务能够访问这个资源。

互斥使用了和信号灯相同的 API 函数,所以也可以指定阻塞时间。阻塞时间代表了任务因为试图获取一个不可马上使用的信号而进入阻塞状态的最大 '节拍' 数。和二进制信号灯不同 - 互斥采用了优先级继承关系。这意味着如果高优先级任务因为试图获取被低优先级任务保持的互斥信号(令牌)而阻塞,那么低优先级任务的优先级将临时上升到被阻塞的任务。这个机制保证了高优先级任务被阻塞的时间最短,并减少了已经发生的 '优先级反转'。

优先级继承并不能解决优先级反转的问题!它只是在某些条件下降低了它的影响。硬实时系统要把防止优先级反转放在第一位来考虑。


6.gif 
使用互斥来保护共享的资源


--------------------------------------------------------------------------------

递归互斥
一个使用了递归的互斥可以反复被所有者 '获取'。互斥变为不可用直到所有者为每次 xSemaphoreTakeRecursive() 请求调用 xSemaphoreGiveRecursive()。例如,如果任务成功 '获取' 相同的互斥 5 次,然后互斥将对其他任务变为不可用,直到它正好 '返还' 互斥 5 次。
这个类型的信号灯使用了优先级继承机制,所以任务 '获取' 信号后必须总是在不再需要信号后 '返还'。

互斥类型信号不能用于中断服务程序。

 

 

原文链接:点击打开

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值