消息队列的使用和原理
消息队列的创建及其如何使用
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t, key, int msgflg); //创建和访问消息队列
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg); //发送消息
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg); //获取消息
我们可以通过msgget()函数来创建消息队列,它会在内核空间创建一个消息链表,msgsend()函数往消息队列发送消息,msgrcv()函数获取消息队列里的数据。
通过消息发送和接收函数可以看到,消息队列的每个消息消息都有msgid,msgtype,msgflg字段。
msgid是消息的队列标识符,msgtype是消息的类型,发送函数中放在msg_ptr这个结构体里,msgflg用来控制读取消息时队列已满或者队列为空时的操作。
消息队列的数据结构,它是一个消息的链表,存放在内核中并由消息队列标识符标识,也就是上面提到的msgid,标识符标识用大于0的整数表示,并且每中标识符的消息队列都有自己的链表。它的表现结构如下图:
消息队列有哪些优点呢?它克服了Linux早期IPC机制的很多缺点,比如消息队列具有异步能力,又克服了具有同样能力的信号承载信息量少的问题;具有数据传输能力,又克服了管道只能承载无格式字节流以及缓冲区大小受限的问题。
但是缺点是消息队列比信号和管道都要更加重量,在内核中会使用更多内存,并且消息队列能传输的数据也有限制,一般上限都是16kb。
消息队列在Android中的使用场景
受限于性能,数据量等问题的限制,Android系统没有直接使用Linux消息队列来进行IPC的场景,但是有大量的场景都利用了消息队列的特性来设计通信方案,比如我们最频繁使用的Handler,就是一个消息队列,由于Handler只是进程内的通信方式,所以它的实现不在这儿讨论,消息队列的架构模型被非常多的场景使用,主要有下面几个有点原因。
- 解耦:消息队列可以实现两个模块之间的解耦,两个需要通信的模块不需要之间对接,发送方只需要将消息丢到队列,接收方只需要从队列里面取消息。
- 异步:我们可以将多条消息并行的发送给消息队列,然后不同的模块去并行的处理,在这种方案下,我们不需要串行的处理任务。
- 缓冲:消息队列的数据结构就是一个缓冲池,可以帮助我们减轻流量过大的压力。
5.1 消息队列也称为报文队列
- 消息队列也成为报文队列,消息队列是随内核持续的,只有在内核重启或者显示删除一个消息队列时,该消息队列才会真正删除。
- 系统中记录消息队列的数据结构体 struct ipc_ids_msg_ids位于内核中,系统中所有消息队列都可以在结构msg_ids中找到访问入口。
5.2 消息队列的原理及注意事项
- 消息队列其实就是一个消息的链表
- 每个消息都有一个队列头,称为struct_msg_queue,这个队列头描述了消息队列的key值,用户ID,组ID等信息,但它存于内核中
- 结构体struct msqid_ds能够返回或设置消息队列的信息,这个结构体位于用户空间中,与msg_queue结构相似的消息队列允许一个或多个进程向它写入或读取消息,消息队列是消息的链表。
- 消息是按消息类型访问,进程必须指定消息类型来读取消息,同样,当向消息队列中写入消息事业必须给出消息的类型,如果读队列使用消息的类型为0,则读取队列中的第一条消息。
- 内核空间的结构体msg_queue描述了对应key值消息队列的情况,而对应用户空间的msqid_ds这个结构体,因此,可以操作msgid_ds这个结构体来操作消息队列。
网友评论