这几天一直在讲进程间通讯的方式,现在来讲下另一种进程间通讯的方式——消息队列,消息队列是一种临时存储消息的队列,完成进程间数据传递的优先级队列。
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个数据类型,接收数据块的进程可以独立的接收含有不同数据类型的数据结构。我们可以发送消息来避免有名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。
Linux用宏MSGMAX和MSGMNB来限制一条消息的最大长度。
每种进程通信方式实现方式和功能不一样,带来适用的场景也有所不同:
消息队列是链表队列,它通过内核提供一个struct msqid_ds *msgque[MSGMNI]向量维护内核的一个消息队列列表,因此linux系统支持的最大消息队列数由msgque数组大小来决定,每一个msqid_ds表示一个消息队列,并通过msqid_ds.msg_first、msg_last维护一个先进先出的msg链表队列,当发送一个消息到该消息队列时,把发送的消息构造成一个msg结构对象,并添加到msqid_ds.msg_first、msg_last维护的链表队列,同样,接收消息的时候也是从msg链表队列尾部查找到一个msg_type匹配的msg节点,从链表队列中删除该msg节点,并修改msqid_ds结构对象的数据。msgque[MSGMNI]是一个msqid_ds的指针数组,每一个msqid_ds结构指针代表一个系统消息队列,msgque[MSGMNI]的大小为MSGMNI=128,也就是说系统中最多有MSGMNI=128个消息队列。
消息队列的操作:
创建或获取:
intmsgget((key_t)key, int flag );
flag:权限,以及控制,IPC_CREAT如果在直接获取,如果不在就创建
发送消息:
intmsgsnd(int msgid, void* ptr, size_t, int flag);
ptr:ptr指向一个结构体:类型+数据
size:数据的大小
flag:一些权限,当队列满时会做一些设置
获取消息:
intmsgrcv(int msgid, void *ptr, size_t, long type, int flag);
long type:选择接收的数据类型
删除消息队列:
Intmsgctl(int msgid, int cmd, struct msgid_ds* buff);
消息队列与有名管道的比较
消息队列跟有名管道有不少的相同之处,与有名管道一样,消息队列进行通信的进程可以是不相关的进程,同时它们都是通过发送和接收的方式来传递数据的。在有名管道中,发送数据用write,接收数据用read,则在消息队列中,发送数据用msgsnd,接收数据用msgrcv。而且它们对每个数据都有一个最大长度的限制。
与有名管道相比,消息队列的优势在于,A. 消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。B. 同时通过发送消息还可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。C. 接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地接收。
消息队列与信号量对比:
都以内核对象来确保多进程访问同一个消息队列,信号量进行同步控制,消息队列发送实际数据。