管道和共享内存:字节流数据
消息:数据报(类型+数据)
队列:优先级队列
可以指定类型来读取,在相同类型下,按照先进先出的顺序
因此:消息队列就是一个消息的链表,每个消息都有一个队列头,在内核中存放着消息队列的数据结构(struct ipc_ids msg_ids),系统中所有消息队列都可以在结构msg_ids中找到访问入口。消息队列用struct msg_queue来描述,队列头中包含了该消息队列的大量信息,包含消息队列键值、用户ID、组ID和消息队列中的消息数目等,甚至记录了最近对消息队列读写进程的ID,读者可以访问这些消息,甚至可以设置其中的,某些信息。
上图描述了内核与消息队列建立起联系的机制,,每一个全局数据结构struct ipc_ids msg_ids都可以访问每个消息队列头的第一个成员struct kern_ipc_perm ,而每一个struct kern_ipc_perm能够与具体的消息队列对应起来,因为在该结构中,有一个key_t成员,它能够唯一确定一个消息队列
struct kern_ipc_perm
{
key_t key; //关键字
//对象拥有者对应进程的有效用户识别号和有效组识别号
uid_t uid;
gid_t gid;
//对象创建者对应进程的有效用户识别号和有效组识别号
uid_t cuid;
gid_t cgid;
mode_t mode; //存取模式
unsigned long seq; // 序列号
}
消息队列的API:
①ftok函数
该函数一般不直接对消息队列操作,但是在调用ipc(MSGGET,…)或msgget来获取消息队列描述字前,往往要调用该函数
②创建和访问一个消息队列
#include
#include
#include
注意:从队列中取出一个消息最重要的依据就是消息的类型,因此对于发送消息时,需要预置一个msgbuf缓冲区并写入消息的类型和内容,调用相应的发送函数即可,对于读取消息,首先分配一个msgbuf缓冲区,然后把消息读入缓冲区即可。
int msgget(key_t key, int msgflag);
key
:某个消息队列的名字,给一个整型值即可,即消息队列唯一的键值,也可用ftok产生,msgflag
:有两个选项IPC_CREAT和IPC_EXCL,单独使用IPC_CREAT,如果消息队列不存在则创建之,如果存在则打开返回;单独使用IPC_EXCL是没有意义的;两个同时使用,如果消息队列不存在则创建之,如果存在则出错返回。返回值
:成功返回一个非负整数,即消息队列的标识码,失败返回-1int msgsnd(int msqid, const void *msqp, size_t msqsz, int msqflg);
msgid
:由msgget函数返回的消息队列标识码msqp
:指针指向准备发送的消息msqsz
:消息结构中数据部分的大小,不包括类型大小msgflg
:默认为0返回值
:成功返回0,失败返回-1消息结构一方面必须小于系统规定的上限,另一方面必须以一个long int长整型开始,接受者以此来确定消息的类型
struct msgbuf
{
long mtye; //消息类型,必须大于0
char mtext[1]; //消息数据
};
3.从一个消息队列接受消息
ssize_t msgrcv(int msqid, void *msqp, size_t msqsz, long msqtyp, int msqflg);
参数:与msgsnd相同
msgid
:由msgget函数返回的消息队列标识码msqp
:消息返回后存储在msqp执行的msgbuf结构中msqsz
:消息结构中消息内容的长度,不包括类型大小msgflg
:默认为04.消息队列的控制函数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid
:由msgget函数返回的消息队列标识码cmd
:有三个可选的值IPC_STAT :该命令用来获取消息队列的队列信息,返回的信息存储在buf指向的msqid结构中;
IPC_SET :该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中,可设置的属性包括msg_perm.uid,msg_perm.gid,msg_perm.mode和msg_qbytes,同时也影响msg_ctime
成员
IPC_RMID 删除msqid标识的消息队列
返回值
:成功返回0,失败返回-1和之前一样,我们写两个程序,a向消息队列中写入数据,b从消息队列中读取数据。
先连续发送三条消息:
然后,连续接收两个消息后发现消息队列中还剩下最后一条消息:
一旦消息队列中的消息被读完或者,读取不存在的消息,比如2号类型消息,那么终端显示就是阻塞状态
注意
:在接收消息的时候,如果将消息类型改为0,不代表接收0号消息,而是不区分消息类型,直接接收,最后靠接受者自己使用
msg.h
#program once
typedef struct msgbuf
{
long mtype;
char mtext[128];
}MsgBuf;
发送
#include
#include
#include
#include
#include
#include
#include
#include
#include"msg.h"
int main(int argc,char *argv[])
{
//argv[1]:type
//argv[2]:data
if(argc < 3)
{
printf("Please input type and data\n");
exit(0);
}
MsgBuf mess;
memset(&mess,0,sizeof(mess));
sscanf(argv[1],"%d",&mss.mtype);
strcpy(msss.mtext,argv[2]);
int msgid = msgget((key_t)1234,IPC_CREAT | 0664);
assert(msgid != -1);
msgsnd(msgid,&mess,strlen(mess,mtext),0);
exit(0);
}
接收(注意读取问题)
#include
#include
#include
#include
#include
#include
#include
#include
#include"msg.h"
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("please input type\n");
exit(0);
}
MsgBuf mess;
memset(&mess,0,sizeof(mess));
long type = 0;
sscanf(argv[1],"%d",&type);
int msgid = msgget((key_t)1234,IPC_CREAT | 0664);
assert(msgid != -1);
msgrcv(msgid,&mess,127,type,0);//改成5看看结果,消息截取
printf("type:%d,data :%s\n",mess.mtype,mess.mtext);
exit(0);
}
注意:每一个消息队列的容量都有限制,该值因为系统不同而不同;另一个限制就是每个消息所能容纳的最大消息数,收消息队列的容量制约,消息个数要小于消息队列的容量(字节数)。