ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

关于RTOS中信号量、互斥量、邮箱、消息队列的一些理解

2021-11-21 12:02:39  阅读:281  来源: 互联网

标签:rt 优先级 队列 RTOS 信号量 互斥 任务 邮箱


1. 信号量

信号量有两种:计数性信号量和二值信号量,计数性信号量可以被获取多次,二值信号量只有0和1两种状态,只能被获取一次。

信号量可以用来对资源进行保护,防止多个任务同时访问某个资源。为资源创建一个专属的二值信号量,任务在申请访问一个资源之前,先申请获取信号量,如果当前没有任务正在访问资源,则获取信号量可以成功,可以继续访问资源;如果当前资源正在被某个任务访问,则获取信号量会失败,任务进入挂起状态,等待其它任务访问资源完成并释放信号量后再访问资源。

如果某个资源允许多个任务同时访问,可以采用计数性信号量,每次有任务申请获取信号量则信号量计数减一,直到减为0后不再允许其他任务再申请获取,即可以通过信号量的初始值来控制同时访问资源的最大任务数量。

2. 优先级反转

使用信号量可能会导致优先级反转的问题,详细的说明见下图,截取自RT-Thread编程指南,简单来说就是当一个高优先级任务申请访问资源时,资源正在被低优先级任务占用,所以高优先级任务被挂起,此时一个中优先级任务就绪,剥夺低优先级任务的CPU使用权,开始执行,就造成了该任务先于高优先级任务开始执行。
在这里插入图片描述

3. 互斥量

互斥量可以理解为二值信号量,它可以避免上述优先级反转的问题,方法是当高优先级任务申请访问资源但资源被低优先级任务时,将低优先级任务的优先级提高到高优先级同级别,就避免了中优先级任务就绪时剥夺低优先级任务的CPU使用权。详细的说明见下图。任务在申请获取互斥量时,系统会判断当前占有该互斥量的任务的优先级和申请任务的优先级,如果申请任务优先级更高,系统会把这个更高的优先级赋给占有互斥量的任务,当任务释放互斥量时再恢复其原有的优先级。
在这里插入图片描述
此外,互斥量还支持重入,即多次获取,这时互斥量被获取了多少次就需要释放多少次,才能完全释放。且互斥量是归属于某个任务的,哪个任务获取互斥量就要由哪个任务释放互斥量,其它任务不能释放。

4. 死锁

死锁也称抱死,是指两个任务无限制地互相等待对方正在占用的资源。详细说明如下,截取自《嵌入式实时操作系统uC/OS-III》:
在这里插入图片描述
避免死锁有以下三个方法:

  • (1)不同任务中用相同的顺序申请多个资源;
  • (2)任务中需要用到多个资源时,先获取所有资源,再做下一步工作;
  • (3)在申请获取互斥量或信号量时设置超时时间,避免永远等待。

最有效的是方法(1),按相同的顺序访问资源,就可以避免两个任务和两个资源间的交叉访问,如果任务中使用资源的顺序固定,那么可以先获取所有资源的访问权限,再访问资源,这样就能避免死锁的问题。方法(3)只是能暂时避免死锁,但会导致系统报错,不推荐。

5. 邮箱和消息队列

5.1 uCOS

uCOS-II有邮箱和消息队列两个概念,都是Event的一种,对于邮箱来说,Event控制块中有一个指针直接指向数据地址,所以每次只能传递一个数据指针。而对消息队列来说,Event控制块中的指针指向一个消息队列,消息队列中的每一个节点包含一个数据地址,所以能同时传递多个数据地址。队列大小为1的消息队列与邮箱的作用是一样的。

uCOS-II中有一个静态的全局结构体数组OSQTbl[OS_MAX_QS],包含了系统中所有可用的消息队列,每创建一个消息队列,就从这个数组中取一个节点出来。

在uCOS-III中,取消了邮箱的概念,只有消息队列。在代码中,定义了一个消息池:

OS_MSG         OSCfg_MsgPool       [OS_CFG_MSG_POOL_SIZE];

所有消息队列在发送消息的时候,都从消息池里取一个节点出来,挂到消息队列的消息链表上,消息队列控制块有指针指向消息链表的头和尾,还会记录消息节点的数量。

消息队列控制块的定义如下:

struct  os_msg_q {                                          /* OS_MSG_Q                                               */
    OS_MSG              *InPtr;                             /* Pointer to next OS_MSG to be inserted  in   the queue  */
    OS_MSG              *OutPtr;                            /* Pointer to next OS_MSG to be extracted from the queue  */
    OS_MSG_QTY           NbrEntriesSize;                    /* Maximum allowable number of entries in the queue       */
    OS_MSG_QTY           NbrEntries;                        /* Current number of entries in the queue                 */
    OS_MSG_QTY           NbrEntriesMax;                     /* Peak number of entries in the queue                    */
};

5.2 rt-thread

RTT中有邮箱和消息队列两个概念,它的邮箱和uCOS-II的邮箱概念不同。RTT的邮箱是基于一个数组来实现的,数组的成员是4字节变量,可以是数据地址或变量,这个数组就是该邮箱的消息池,在创建邮箱的时候固定进行分配。所以RTT的邮箱在传递数据时每次固定传输一个4字节的数据,每次发送邮件的时候从消息池中取一个4字节变量出来,接收邮件的时候释放一个4字节变量。邮箱控制块中存储着当前邮箱发送邮件(in_offset)和接收邮件(out_offset)的数据地址,以及当前邮箱中存在的邮件数量(entry)。

邮箱控制块的定义如下:

struct rt_mailbox
{
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */

    rt_ubase_t          *msg_pool;                      /**< start address of message buffer */

    rt_uint16_t          size;                          /**< size of message pool */

    rt_uint16_t          entry;                         /**< index of messages in msg_pool */
    rt_uint16_t          in_offset;                     /**< input offset of the message buffer */
    rt_uint16_t          out_offset;                    /**< output offset of the message buffer */

    rt_list_t            suspend_sender_thread;         /**< sender thread suspended on this mailbox */
};

RTT消息队列的实现和uCOS相似。但RTT不再是所有消息队列共用一个消息池,而是每个消息队列单独有一个消息池,分别管理。

RTT的消息队列控制块定义如下:

struct rt_messagequeue
{
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */

    void                *msg_pool;                      /**< start address of message queue */

    rt_uint16_t          msg_size;                      /**< message size of each message */
    rt_uint16_t          max_msgs;                      /**< max number of messages */

    rt_uint16_t          entry;                         /**< index of messages in the queue */

    void                *msg_queue_head;                /**< list head */
    void                *msg_queue_tail;                /**< list tail */
    void                *msg_queue_free;                /**< pointer indicated the free node of queue */

    rt_list_t            suspend_sender_thread;         /**< sender thread suspended on this message queue */
};

标签:rt,优先级,队列,RTOS,信号量,互斥,任务,邮箱
来源: https://blog.csdn.net/weixin_42967006/article/details/121445558

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有