1.概述
SystemV 消息队列使用消息队列标识符标识,这个标识好像链表中的头节点,包含了许多信息,当然最主要的还是指向数据节点的两个指针msg_first
和msg_last
,分别表示消息队列中的第一个和最后一个消息节点。
2.消息队列函数
操作SystemV消息队列的函数。
2.1 msgget函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
其中key
既可以是IPC_PRIVATE
,也可以是ftok
函数的返回值。当key
的值为IPC_PRIVATE
或者当前没有消息队列与给定key
相关联时,将创建一个新的消息队列。
msgflg
指定获取消息队列时的权限组合:当它被指定IPC_CREAT
和IPC_EXCL
时且跟key
关联的消息队列已经存在,则msgget
调用失败且设置errno=EEXIST
2.2 msgsnd函数
msgget
打开一个消息队列后,我们使用msgsnd往其放一个消息,好像链表插入新节点。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid
是msgget返回的标识符
msgp
是一个结构指针,它有一个模板:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
不过一般情况下,应用通常使用自己定义的消息类型,消息内容也不仅仅局限于文本,因为对于msgp
来说,它只是一个指针而已。
2.3 msgrcv函数
从消息队列中读出一个消息。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
其中msgp
参数指定接收消息的存放位置,与msgsnd
一样,该指针指向紧挨在真正的消息数据之前返回的长整数类型字段。
msgsz
指定了数据大小,不包含类型字段。
msgtyp
指定读出消息的类型。
msgflg
指定请求类型消息不在队列时怎么处理。
成功返回时,将返回接收消息中数据的字节数,不含消息类型的那几个字节。
3. System V消息队列编程
依然是实现简单生产者消费者模型,不同管道,某个进程往一个队列中写入一个消息之前,不求另一个某个进程正在等待该队列上的一个消息到达。
数据结构定义
//msg.h
#ifndef __MSG_H__
#define __MSG_H__
#define MAXSIZE 256
struct Message_
{
long mtype;
unsigned int mlength;
char buff[MAXSIZE];
};
typedef struct Message_ Message;
#endif
发送方(生产者)
//sysV_send.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include "msg.h"
int main(int argc,char **argv)
{
const char * const pathname="/home/zhangxiao/zxtest/systemV/";//ftok所需的pathname
int msqid;
key_t key;
int mflag;
Message msg;
//0600 拥有者拥有读写权限。
mflag=IPC_EXCL|IPC_CREAT|0600;
key = ftok(pathname,1);//ftok 获取 key
if(key<0)
{
fprintf(stderr, "ftok: %s\n", strerror(errno));
return -1;
}
msqid=msgget(key,mflag);//msgget函数调用
if((msqid < 0) && (errno!=EEXIST))//msgget失败且不是因为消息队列已经存在
{
fprintf(stderr, "msgget: %s\n", strerror(errno));
return -1;
}
if(msqid < 0)//消息队列已经存在
{
printf("debug...\r\n");//说明打开的消息队列已经存在
msqid=msgget(key,IPC_CREAT|0600);//将忽视IPC_CREAT关键字
if (msqid < 0) {
fprintf(stderr, "msgget: %s\n", strerror(errno));
return -1;
}
}
memset(&msg,0x00,sizeof(Message));
strcpy(msg.buff,"HelloWorld666");
msg.mlength=strlen(msg.buff);
int i;
for(i = 0;i < 3;i++ )
{
msg.mtype=(i+1)*100;
if(msgsnd(msqid,(void *)&msg,sizeof(Message),0)<0)//发送消息
{
fprintf(stderr, "msgsnd: %s\n", strerror(errno));
return -1;
}
}
return 0;
}
接收方(消费者)
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include "msg.h"
int main(int argc,char **argv)
{
const char * const pathname="/home/zhangxiao/zxtest/systemV/";
int msqid;
key_t key;
key = ftok(pathname,1);//ftok 获取 key
Message msg;
ssize_t n;
if(key<0)
{
fprintf(stderr, "ftok: %s\n", strerror(errno));
return -1;
}
msqid=msgget(key,IPC_CREAT);//将忽视IPC_CREAT关键字
if (msqid < 0)
{
fprintf(stderr, "msgget: %s\n", strerror(errno));
return -1;
}
memset(&msg,0x00,sizeof(Message));
int i;
for(i=2;i>=0;i--)
{
n=msgrcv(msqid,&msg,sizeof(Message),(i+1)*100,0);
printf("msgtype=%ld,message=%s\r\n",msg.mtype,msg.buff);
}
msgctl(msqid,IPC_RMID,0);//删除消息队列
return 0;
}
Makefile
# Makefile
PROGS =Send Recv
CLEANFILES = core core.* *.core *.o temp.* *.out typescript* \
*.lc *.lh *.bsdi *.sparc *.uw
all :${PROGS}
CFLAGS+=-g
LIBS+=
Recv: sysV_recv.o
@${CC} ${CFLAGS} ${LIBPATH} $^ -o $@ ${LIBS}
@rm *.o
Send: sysV_send.o
@${CC} ${CFLAGS} ${LIBPATH} $^ -o $@ ${LIBS}
@rm *.o
clean:
rm -f ${PROGS} ${CLEANFILES}
4.补充
终端可以使用ipcs
等命令查看和操作systemV IPC对象。