RT-thread内核之消息队列
一、消息队列控制块:在include/rtdef.h中
#ifdef RT_USING_MESSAGEQUEUE /** * message queue structure */ struct rt_messagequeue { struct rt_ipc_object parent; /**< inherit from ipc_object */ //继承自IPC对象 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 *///指向空闲队列 }; typedef struct rt_messagequeue *rt_mq_t; #endif
以上是消息队列控制块的定义,那么消息队列内部包含的每条消息元素又是如何定义的呢?在src/ipc.c中定义:
struct rt_mq_message { struct rt_mq_message *next;//指向下一条消息元素 }; 可能大家会有个疑问,怎么消息元素没有消息的具体数据内容呢? 其实上面这个消息元素只能算作是消息头,就好比短信的标题一般,真正内部跟随在这个消息头后面,一直到下一个消息头之前的所有内存中的数据就是消息的真正内容。rt-thread采用静态队列的方式来实现消息队列的IPC功能的。消息头只是用来做索引功能,通过它可以索引到下一个消息头的位置。如果大家还是有些不明白,那么可以先看看消息队列是如何初始化的,就会明白为什么会有这么一个消息头了。
二、消息队列相关接口:在src/ipc.c中:
创建消息队列: rt_mq_t rt_mq_create(const char *name, //消息队列的名称 rt_size_t msg_size, //消息队列中每条消息的最大长度 rt_size_t max_msgs, //消息队列的最大容量 rt_uint8_t flag); //消息队列采用的等待方式(FIFO/PRIO) 创建消息队列时先创建一个消息队列对象控制块,然后给消息队列分配一块内存空间,组织成空闲消息链表,这块内存的大小等于[消息大小+消息头(用于链表连接)]与消息队列容量的乘积,接着再初始化消息队列,此时消息队列为空。 删除消息队列: rt_err_t rt_mq_delete(rt_mq_t mq); 删除消息队列时,如果有线程被挂起在该消息队列等待队列上,则内核先唤醒挂起在该消息等待队列上的所有线程(返回值是 -RT_ERROR),然后再释放消息队列使用的内存,最后删除消息队列对象。
初始化消息队列: rt_err_t rt_mq_init(rt_mq_t mq, //指向静态消息队列对象的句柄 const char *name, //消息队列的名称 void *msgpool, //用于存放消息的缓冲区首地址 rt_size_t msg_size, //消息队列中每条消息的最大长度 rt_size_t pool_size, //存放消息的缓冲区大小 rt_uint8_t flag); //消息队列采用的等待方式(FIFO/PRIO) 初始化消息队列时,该接口需要获得消息队列对象的句柄(即指向消息队列对象控制块的指针)、消息队列名、消息缓冲区指针、消息大小以及消息队列容量。消息队列初始化后所有消息都挂在空闲消息列表上,消息队列为空。 脱离消息队列: rt_err_t rt_mq_detach(rt_mq_t mq); 使用该函数接口后,内核先唤醒所有挂在该消息等待队列对象上的线程(返回值是-RT_ERROR ),然后将该消息队列对象从内核对象管理器中删除。
发送消息: rt_err_t rt_mq_send(rt_mq_t mq, void *buffer, rt_size_t size); 线程或者中断服务程序都可以给消息队列发送消息。当发送消息时,消息队列对象先从空闲消息链表上取下一个空闲消息块,把线程或者中断服务程序发送的消息内容复制到消息块上,然后把该消息块挂到消息队列的尾部。当且仅当空闲消息链表上有可用的空闲消息块时,发送者才能成功发送消息;当空闲消息链表上无可用消息块,说明消息队列已满,此时,发送消息的的线程或者中断程序会收到一个错误码(-RT_EFULL)。 发送消息时,发送者需指定发送到的消息队列的对象句柄(即指向消息队列控制块的指针),并且指定发送的消息内容以及消息大小。在发送一个普通消息之后,从空闲消息链表上取下的队首消息块被转移到了消息队列队尾。 发送紧急消息: rt_err_t rt_mq_urgent(rt_mq_t mq, void *buffer, rt_size_t size); 发送紧急消息的过程与发送消息几乎一样,唯一的不同是,当发送紧急消息时,从空闲消息链表上取下来的队首消息块不是挂到消息队列的队尾,而是挂到消息队列的队首,这样接收者就能够优先接收到紧急消息,从而及时进行消息处理。 接收消息: rt_err_t rt_mq_recv(rt_mq_t mq, //消息队列对象的句柄 void *buffer, //用于接收消息的数据块 rt_size_t size, //消息大小 rt_int32_t timeout); //指定的超时时间 当消息队列中有消息时,接收者才能接收消息,否则接收者会根据超时时间设置或挂起在消息队列的等待线程队列上,或直接返回。 接收消息时,接收者需指定存储消息的消息队列对象句柄,并且指定一个内存缓冲区,接收到的消息内容将被复制到该缓冲区里。此外,还需指定未能及时取到消息时的超时时间。
接收一个消息后消息队列上的队首消息被转移到了空闲消息链表的尾部。
控制消息队列: rt_err_t rt_mq_control(rt_mq_t mq, rt_uint8_t cmd, void *arg); 只支持RT_IPC_CMD_RESET这一条命令,表示复位消息队列。