消息队列
消息队列提供了一种从一个进程向另一个进程发送数据块的方法。每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息来避免命名管道的同步和阻塞问题。消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出。消息队列与命名管道有一样的不足,就是每个消息的最大长度是有上限的,每个消息队列的总的字节数是有上限的,系统上消息队列的总数也有一个上限。
IPC对象数据结构
内核为每个IPC对象维护了一个数据结构:
struct ipc_perm{
key_t _key; /*Key supplied to xxxget(2)*/
uid_t uid; /*Effective UID of owner*/
gid_t gid; /*Effective GID of owner*/
uid_t cuid; /*Effective UID of owner*/
gid_t cgid; /*Effective GID of owner*/
unsigned short mode; /*Permissions*/
unsigned short _seq; /*Sequence number*/
}
消息队列结构
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes in
queue (non-standard) */
msgqnum_t msg_qnum; /* Current number of messages
in queue */
msglen_t msg_qbytes; /* Maximum number of bytes
allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
};
第一个条目就是IPC结构体,消息队列,共享内存,信号量都有一个这样共同的数据结构。后面的都是消息队列所 私 有的成员。消息队列是用链表来实现的。
在Linux中使用消息队列
Linux提供了一系列的消息队列的函数接口来让我们方便地使用它来实现进程间的通信。它的用法与其他两个System V IPC机制,即信号量和共享内存相似。
1.msgget函数:该函数用来创建和访问一个消息队列。
原型:int msgget(key_t key , int msgflg)
与其他IPC机制一样,程序必须提供一个键来命名某个特定的消息队列,key可以认为是一个端口号,也可以由ftok生成。msgflg是一个权限标志,表示消息队列的访 问权限,它与文件的访问权限一样。IPC_CREAT表示如果IPC不存在,则创建一个IPC资源,否则打开操作;IPC_EXCL只有在共享内存不存在的时候,新的共享内存才创建,否则就产生错误。如果将IPC_CREAT和IPC_EXCL标志一起使用,XXXget()将返回一个新建的IPC标识符,若该资源已存在,返回-1。IPC_EXEL标志本身并没有太大的意义,但和IPC_CREAT标志一起使用可以用来保证所得的对象都是新建的,而不是已有的对象。
2.msgrcv函数:从队列中取用消息
原型:sszie_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg)
msgsnd函数:将数据放到消息队列中
原型:int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg)
msqid是消息队列的标识码,msgp指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:
struct msgstru{
long mtype; //大于0
char mtext[用户指定大小];
}
msgsz消息的大小。msgtyp从消息队列内读取消息形态,如果值为零,则表示消息队列中的所有消息都会被读取。 msgflg用来指明核心程序在队列没有数据的情况下所应采取的行动。如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflag为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采用阻塞等待的处理模式。
3.msgctl函数:设置消息队列属性。
原型:int msgctl(int msgqid,int cmd,struct msqid_ds *buf)
msgctl系统调用对msgqid标识的消息队列执行cmd操作,系统定义了3种cmd操作:IPC_STAT:该命令用来获取消息队列对应的msqid_ds数据结构,并将其保存到buf指定的地址空间;IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf中;IPC_RMID:从内核中删除msqid标识的消息队列。
下面我们来看下面代码,server.c创建消息队列,接收数据,client.c发送数据。
//comm.h
#ifndef _COMM_H
#define _COMM_H
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#include<unistd.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
struct msgbuf{
long mtype;
char mtext[1024];
};
int Creat_msgQueue();
int Get_msgQueue();
int Delete_msgQueue(int msg_id);
int sendData_To_msgQueue(int msg_id,int sendType,char* msg);
int recData_To_msgQueue(int msg_id,int receType,char* out);
#endif
//comm.c
#include"comm.h"
static int commCreat_msgQueue(int flags)
{
key_t key=ftok(PATHNAME,PROJ_ID);
if(key<0){
perror("ftok");
return -1;
}
int msg_id=msgget(key,flags);
if(msg_id<0){
perror("msgget");
return -2;
}
return msg_id;
}
int Creat_msgQueue()
{
return commCreat_msgQueue(IPC_CREAT|IPC_EXCL|0666);
}
int Get_msgQueue()
{
return commCreat_msgQueue(IPC_CREAT);
}
int sendData_To_msgQueue(int msg_id,int sendType,char* msg)
{
struct msgbuf buf;
buf.mtype=sendType;
strcpy(buf.mtext,msg);
int snd_msg=msgsnd(msg_id,(void*)&buf,sizeof(buf),0);
if(snd_msg<0){
perror("msgsnd");
return -3;
}
return 0;
}
int recData_To_msgQueue(int msg_id,int receType,char* out)
{
struct msgbuf buf;
ssize_t rev_msg=msgrcv(msg_id,(void*)&buf,sizeof(buf),receType,0);
if(rev_msg<0){
perror("msgrcv");
return -4;
}
strcpy(out,buf.mtext);
return 0;
}
int delete_msgQueue(int msg_id)
{
if(msgctl(msg_id,IPC_RMID,NULL)<0){
perror("msgctl");
return -5;
}
return 0;
}
//server.c
#include"comm.h"
int main()
{
char buf[1024];
int msg_id=Creat_msgQueue();
while(1){
//receive data
recData_To_msgQueue(msg_id,CLIENT_TYPE,buf);
printf("Client#%s\n",buf);
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s<=0){
perror("read");
return -1;
}else{
buf[s-1]=0;
sendData_To_msgQueue(msg_id,SERVER_TYPE,buf);
printf("send done,wait receive...\n");
}
}
return 0;
}
//client.c
#include"comm.h"
int main()
{
int msg_id=Get_msgQueue();
char buf[1024];
while(1){
printf("Please Enter#\n");
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s<0){
perror("read");
}
buf[s-1]=0;
sendData_To_msgQueue(msg_id,CLIENT_TYPE,buf);
printf("send done,wait receive...\n");
recData_To_msgQueue(msg_id,SERVER_TYPE,buf);
printf("Server#%s\n",buf);
}
delete_msgQueue(msg_id);
return 0;
}
上面的的程序用消息队列实现了进程间通信,下面我们在两个终端下查看运行结果:
以上就是关于消息队列的简单介绍,若有不足,请指出。