今天在学习XSI IPC 编程时,发现APUE上的消息队列这一章节讲解的很粗略,阅读两遍后仍是一头雾水。被逼无奈,只好求助百度。在查看了很多前辈们的文章后,终于有所领悟。
其中,http://blog.csdn.net/zhsp1029/article/details/2171462 zhsp1029的博客讲解的很好,今天就以此文章为基础,加入一些自己的见解。
对消息队列的操作有下面三种类型:
1、 打开或创建消息队列
消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,只需提供该消息队列的键值即可;
注:消息队列描述字是由在系统范围内唯一的键值生成的,可将消息队列描述字对应linux操作文件时的文件描述符,而键值可以看作对应系统内的一条路经。
2、 读写操作
消息读写操作非常简单,对开发人员来说,每个消息都类似如下的数据结构:
struct msgbuf{
long mtype;
char mtext[1];
};
mtype成员代表消息类型,从消息队列中读取消息的一个重要依据就是消息的类型;mtext是消息内容,当然长度不一定为1。因此,对于发送消息来说,首先预置一个msgbuf缓冲区并写入消息类型和内容,调用相应的发送函数即可;对读取消息来说,首先分配这样一个msgbuf缓冲区,然后把消息读入该缓冲区即可。
3、 获得或设置消息队列属性:
消息队列的信息基本上都保存在消息队列数据结构中,因此,可以分配一个消息队列数据结构(struct msqid_ds),来返回消息队列的属性;同样可以设置该数据结构。
消息队列API
1、文件名到键值
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok (char*pathname, char proj);
它返回与路径pathname相对应的一个键值。该函数不直接对消息队列操作,但在调用ipc(MSGGET,…)或msgget()来获得消息队列描述字前,往往要调用该函数。典型的调用代码是:
key=ftok(path_ptr, 'a');
ipc_id=ipc(MSGGET, (int)key, flags,0,NULL,0);
msgid=msgget(key,IPC_CREAT | IPC_EXCL);
2.系统V消息队列API
系统V消息队列API共有四个,使用时需要包括几个头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
1)int msgget(key_t key, int msgflg)
参数key是一个键值,由ftok获得;msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。
在以下两种情况下,该调用将创建一个新的消息队列:
如果没有消息队列与健值key相对应,并且msgflg中包含了IPC_CREAT标志位;
key参数为IPC_PRIVATE;
参数msgflg可以为以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果:
当只有IPC_CREAT选项打开时,若不存在则创建信号量集返回该ID,若存在,则都返回该信号量集的ID,
当只有IPC_EXCL选项打开时,不管有没有该信号量集,shmget()都返回-1
所以当IPC_CREAT | IPC_EXCL时, 如果没有该信号量,则创建,并返回ID。若已有该信号量集,则返回-1;
调用返回:成功返回消息队列描述字,否则返回-1。
注:参数key设置成常数IPC_PRIVATE并不意味着其他进程不能访问该消息队列,只意味着即将创建新的消息队列。
2)int msgrcv(int msqid, struct msgbuf *msgp, int msgsz,long msgtyp, int msgflg);
该系统调用从msgid代表的消息队列中读取一个消息,并把消息存储在msgp指向的msgbuf结构中。
msqid为消息队列描述字;消息返回后存储在msgp指向的地址,
msgsz指定msgbuf的mtext成员的长度(即消息内容的长度),
msgtyp为请求读取的消息类型,取值为:0,正整数,负整数
举个例子:假设消息队列中依次有类型为3的消息2个,类型为4的消息2个,类型为1的消息2个。如果msgtyp=0,则读取类型为3的第一条消息;如果msgtyp=4,则读取类型为4的第一条消息(如果msgflg设置成MSG_EXCEPT,则读取类型为3的第一条消息);如果msgtyp=-3,则读取类型为1的第一条消息。
读消息标志msgflg可以为以下几个常值的或:
IPC_NOWAIT 如果没有满足条件的消息,调用立即返回,此时,errno=ENOMSG
IPC_EXCEPT 与msgtyp>0配合使用,返回队列中第一个类型不为msgtyp的消息
IPC_NOERROR 如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将丢失。
msgrcv()解除阻塞的条件有三个:
消息队列中有了满足条件的消息;
msqid代表的消息队列被删除;
调用msgrcv()的进程被信号中断;
调用返回:成功返回读出消息的实际字节数,否则返回-1。
3)int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, intmsgflg);
向msgid代表的消息队列发送一个消息,即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定。
对发送消息来说,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。造成msgsnd()等待的条件有两种:
当前消息的大小与当前消息队列中的字节数之和超过了消息队列的总容量;
当前消息队列的消息数(单位"个")不小于消息队列的总容量(单位"字节数"),此时,虽然消息队列中的消息数目很多,但基本上都只有一个字节。
msgsnd()解除阻塞的条件有三个:
不满足上述两个条件,即消息队列中有容纳该消息的空间;
msqid代表的消息队列被删除;
调用msgsnd()的进程被信号中断;
调用返回:成功返回0,否则返回-1。
4)int msgctl(int msqid, int cmd, struct msqid_ds *buf);
该系统调用对由msqid标识的消息队列执行cmd操作,共有三种cmd操作:IPC_STAT、IPC_SET 、IPC_RMID。
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。
以下为测试用例
test_server.c
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #defineFILEPATH "/tmp/test_msg.c" #defineBUFSIZE 512 structmsgbuf{ long mtype; char mtext[BUFSIZE]; }; voidremove_msg(void); voidmy_err(char *); intmsgid; intmain(void) { key_t key; struct msgbuf msgbufp; printf("---------------Start-----------------\n"); if((key=ftok(FILEPATH,0))==-1) //创建键值 my_err("ftok error"); if((msgid=msgget(key,IPC_CREAT))==-1)//如果消息队列存在,则打开;否则创建。如果msgflg指定为 IPC_CREAT|IPC_EXCL 则表示如果消息队列不存在,则创建;否则出错返回。errno=EEXIST. my_err("=msggeterror"); atexit(remove_msg); //注册程序退出函数,当退出时,删除消息队列 while(1) { if(msgrcv(msgid,&msgbufp,BUFSIZE,2,0)==-1) my_err("msgrcverror"); printf("client:%s",msgbufp.mtext); printf("server:"); fgets(msgbufp.mtext,BUFSIZE,stdin); msgbufp.mtype=1; if('q'==msgbufp.mtext[0]||'Q'==msgbufp.mtext[0]) exit(0); if(msgsnd(msgid,&msgbufp,BUFSIZE,0)==-1) my_err("msgsnderror"); } exit(0); } //删除创建的消息队列 voidremove_msg(void) { if(msgctl(msgid,IPC_RMID,NULL)==-1) perror("remove msg queueerror"); } //自定义出错函数 voidmy_err(char *str) { fprintf(stderr,"Line:%d,",__LINE__); perror(str); exit(1); }
test_client.c
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #defineFILEPATH "/tmp/test_msg.c" #defineBUFSIZE 512 structmsgbuf{ long mtype; char mtext[BUFSIZE]; }; voidremove_msg(void); voidmy_err(char *); intmsgid; intmain(void) { printf("-------------ClientStart--------------\n"); key_t key; struct msgbuf msgbufp; if((key=ftok(FILEPATH,0))==-1) my_err("ftok error"); if((msgid=msgget(key,0))==-1) my_err("msggeterror"); while(1) { printf("client:"); fgets(msgbufp.mtext,BUFSIZE,stdin); if(msgbufp.mtext[0]=='q'||'Q'==msgbufp.mtext[0]) exit(0); msgbufp.mtype=2; if(msgsnd(msgid,&msgbufp,BUFSIZE,0)==-1) my_err("msgsnderror"); if(msgrcv(msgid,&msgbufp,BUFSIZE,1,0)==-1) my_err("msgrcverror"); printf("server:%s",msgbufp.mtext); } exit(0); } voidremove_msg(void) { if(msgctl(msgid,IPC_RMID,NULL)==-1) perror("remvoe msg queueerror"); } voidmy_err(char *str) { printf("Line:%d,",__LINE__); perror(str); exit(1); }