和信号相比,信息队列是随内核持续的,而信号是随进程持续的,管道和有名管道也是随进程持续的。消息队列是一个消息的链表,可以将消息看作一个记录,具有特定的格式和特定的优先级。 对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新的消息;对消息队列又读权限的进程则可以从消息队列中读走信息。目前主要有两者类型的消息队列:POSIX消息队列和系统V消息队列。系统V消息队列相对来说应用更加广泛,但是考虑到程序的可一致性,还是应尽量开发POSIX消息队列。
本文旨在介绍系统V消息队列的一些常用API。
消息队列API函数及其用途:
msgget : 创建一个新的消息队列 或者 获取消息队列的ID
msgsnd : 向消息队列发送消息
msgrcv : 从消息队列读取消息
msgctl : 获取消息队列的信息 or 设置消息队列的信息 or 移除消息队列。
ftok : 提供文件地址到键值的映射
1,
int ftok(char *pathname, char proj) pathname : 消息队列的文件路径 ; proj : 一般取值为:‘a’
int msgget( key_t key , int msgflag ) key 为ftok函数的返回值,也可以为IPC_PRIVATE指不提供键值 msgflag : 允许用户设置两者类型的参数,一个指令和一个访问权限设置,访问权限的设置和设置文件的访问权限的方式一样。 指令的设置可以设置为:IPC_CREAT(创建一个消息队列),IPC_EXCL(判断消息队列是否已经存在,如果存在 就返回一个错误信息),0(告诉msgget消息队列已经存在,任务是获取消息队列的描述符)
samp1 : 获得消息队列的描述符:
// get the description of message queue msgid = msgget( key , 0 ); if(msgid == -1){ printf("Queue does not exist...\n"); }
samp2 :创建一个新的消息队列:
// Create a new message queue msgid = msgget(key , IPC_CREAT | IPC_EXCL | 0666); //设置访问权限和判断是否存在 if(msgid == -1){ printf("Queue already exists...\n"); } else{ printf("Queue created!\n"); }
3,
#include<sys/msg.h> int msgctl( int msgid , int cmd , struct msqid_ds *buf) ;
主要有三个功能:
Fun 1 :
//移除消息队列 int msgid , ret ; msgid = msgget( key , 0); if( msgid != -1){ ret = msgctl(msgid , IPC_RMID , NULL); if( ret == 0 ){ // Queue was successfully removed! } }
Fun 2 :
//设置消息队列的属性 int msgid, ret ; struct msqid_ds buf ; msgid = msgget( key , 0 ); ret = msgctl(msgid, IPC_STAT ,&buf); if( ret == -1){ fprintf(stderr,"Error:%s\n",strerror(errno)); } else{ printf("Parameters got!\n"); } buf.msg_qbytes = 20000 ; ret = msgctl(msgid , IPC_SET , &buf); if(ret == -1){ fprintf(stderr,"Fail to set queue:%s\n",strerror(errno)); } else{ printf("Parameters changed!\n"); }
int msgsnd( int msgid , struct msgbuf *msgp , size_t msgsz , int msgflag) ; msgp : 一个struct msgbuf结构体,存放要向消息队列中发送的内容。 msgsz : msgbuf 的大小。 msgflag : IPC_NOWAIT向消息队列中发送内容的时候,如果没有足够的空间不阻塞,立即返回。
int msgrcv( int msgid , struct msgbuf *msgp , size_t msgsz , long msgtyp, int msgflag) 前面三个参数和msgsnd一样。 msgtyp : 0 读取消息队列中可用的第一条信息 >0 指定要读取的消息的type <0 返回消息队列中的第一条消息类型号小于或等于msgtyp的绝对值的消息 msgflag : IPC_NOWAIT: 如果消息队列中没有msgtyp的消息,立即返回,不阻断 MSG_EXECPT:返回除msgtyp指定类型以外的第一条消息 MSG_NOERROR:如果用户的缓冲区不够大,直接截去消息中保存不下的部分。
/* 消息队列实例 */ #include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #include<errno.h> #include<unistd.h> #include<string.h> #include<assert.h> #define MAX_LINE 80 struct msqid_ds buf ; /*定义消息的结构*/ typedef struct{ long mtype ; /* 每条消息都需要一个消息的类型,mtype,类型为long型。*/ /* 自定义的消息内容。 */ float fval ; unsigned int uival ; char strval[MAX_LINE+1] ; }MSG_TYPE ; int main(int argc,char *argv[]){ key_t key ; /* 消息队列键值*/ MSG_TYPE my_type ; /* 存放消息的变量*/ int msgid ,ret ; key = ftok("b.txt",'a'); /*由pathname确定一个内核内唯一的key*/ if(key == -1){ printf("Erro1r!:%s\n",strerror(errno)); exit(1) ; } msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666); /* 创建一个消息队列,并设置权限*/ if(msgid == -1){ fprintf(stderr,"Error:%s\n",strerror(errno)); exit(1) ; } ret = msgctl(msgid, IPC_STAT,&buf); /* 获得消息队列队头,即消息队列自身的信息 */ if(ret == -1){ fprintf(stderr,"Error:%s\n",strerror(errno)); exit(1) ; } /* 定义需要写入的信息 */ my_type.mtype = 1L ; my_type.fval = 128.1256 ; my_type.uival = 1291223 ; strncpy(my_type.strval,"This is message queue!\n",MAX_LINE); ret = msgsnd(msgid , (MSG_TYPE *)&my_type,sizeof(MSG_TYPE),0); /* 向消息队列中写入内容 */ if(ret == -1){ fprintf(stderr,"Message send fail!:%s\n",strerror(errno)); exit(1) ; } printf("Write Success!\n"); /* 写入消息成功 */ ret = msgctl(msgid,IPC_STAT,&buf); /* 获得消息队列的信息。 */ assert(ret != -1); printf("current number of bytes on queue:%ld\n",buf.msg_cbytes); /* 消息队列的存储量 */ printf("number of message in queue is : %ld\n",buf.msg_qnum); /* 消息队列中消息的条数 */ printf("Max number of bytes on queue is : %ld\n",buf.msg_qbytes); /* 消息队列的最大存储量*/ printf("\n"); ret = msgrcv(msgid, (MSG_TYPE *)&my_type, sizeof(MSG_TYPE),0,0); /*读取消息队列中的信息。*/ assert( ret!=-1 ) ; printf("Message type : %ld\n",my_type.mtype); printf("Float value: %f\n",my_type.fval); printf("Uint val is : %d\n",my_type.uival); printf("String val is : %s\n",my_type.strval); ret = msgctl(msgid,IPC_RMID,NULL); /*消息队列的撤销*/ assert( ret!=-1 ); printf("Queue %d successfully removed!\n",msgid); return 0; }
在本文的最后,介绍一下命令行的ipcs工具:
查看当前进程可见的所有消息队列: # ipcs -q
查看具体某个消息队列ID的信息: # ipcs - q -i queue_num
删除某个消息队列: # ipcrm -q queue_num