system v消息队列
消息队列可以认为是一个消息链表,对于来说除非有读者存在否则写着是没有意义的,但是消息队列可以往某个队列写入消息,然后终止,等待另一进成在某一时刻去读出这些消息,并且system消息队列可以返回指定的特定优先级消息
对于系统中的每个消息队列,内核维护一个定义在头文件<sys/msg.h>的信息结构
15 struct msqid_ds {16 struct ipc_perm msg_perm;
17 struct msg *msg_first; /* first message on queue,unused */
18 struct msg *msg_last; /* last message in queue,unused */
19 __kernel_time_t msg_stime; /* last msgsnd time */
20 __kernel_time_t msg_rtime; /* last msgrcv time */
21 __kernel_time_t msg_ctime; /* last change time */
22 unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
23 unsigned long msg_lqbytes; /* ditto */
24 unsigned short msg_cbytes; /* current number of bytes on queue */
25 unsigned short msg_qnum; /* number of messages in queue */
26 unsigned short msg_qbytes; /* max number of bytes on queue */
27 __kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
28 __kernel_ipc_pid_t msg_lrpid; /* last receive pid */
29 };
主要的函数介绍:
#include <sys/msg.h>
int msgget(key_t key, int flag);
此函数返回key指定消息的标识符
key 一般有ftok函数产生 ,该函数为key_t ftok(const char *pathname, int proj_id);
该函数把从pathname导出的信息与id低8位组合成一个整数IPC键, 调用时pathname必须存在,若不存在ftok调用失败,返回-1,成功返回该整数IPC键值
flag 为该消息队列的读写权限组合,可以与IPC_CREAT 或IPC_EXCL相与,其中创建对列时都要使用IPC_CREAT,其中IPC_CREAT|IPC_EXCL含义是若已有该队列则返回错误
此函数成功时,返回非负队列标识符;失败时返回-1
int msgsnd(int msqid, const void *ptr, size_t length, int flag);
此函数发送消息到指定的消息对列
其中msgid是msgget返回的消息队列标识符
ptr是一个结构体指针,该结构体有如下模板:
strut msgbuf
{
long msgbuf;
char mtext[1];
}
其中msgbuf必需大于0,因为接受的函数对于负数消息类型用作特殊指示器,当然这个模板因为只能发送1个字符的数据,所以大多数应用都不使用该模板,而是自己定义根据该结构体的结构,例如我自己定义的消息结构
typedef struct my_msgbuf
{
long mtype; //类型
char mchar[MAXSIZE];//消息空间
}Message;
length是结构体中具体数据的长度
flga可以为0或者IPC_NOWAIT。
当指定IPC_NOWAIT时,msgsnd函数成为非阻塞,若发送消息时该队列没有存放消息的可用空间时,该函数立即放回
发送成功返回0,发送失败返回-1.
ssize_t msgrcv(int msqid, void *ptr, size_t length, long type, flag);
该函数接受指定类型的消息
其中msgid是msgget返回的消息队列标识符
ptr为接受消息存放的位置
type指定待接收消息的类型
当flag指定为IPC_NOWAIT时,msgrcv函数成为非阻塞,若消息队列中没有消息时,该函数立即返回,否则阻塞到有消息发送给该消息队列
其中type的规则为:
当type=0时,返回消息队列中的第一个消息,消息队列是作为一个fifo链表维护的
当type>0时,返回类型值为type的第一个消息
当type<0时,返回类型值小于或者等于type绝对值的消息中类型值最小的消息。
msgrcv函数成功时返回为读入缓冲区的字节数,失败返回-1.
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
消息队列控制函数
其中msqid为消息队列描述符
cmd有以下三种:
IPC_RMID:删除msgid指定的消息队列,当前在该队列上的任何消息都被丢弃,对于该命令,buf参数可忽略
IPC_SET:设置消息队列msgid_ds结构体的四个成员:msg_perm.uid,msg_perm_gid,msg_perm.mode和msg_qbytes。它们的值来自由buf指向的结构体中的相应成员。
IPC_STAT:给调用者通过buf返回指定消息队列当前对应msgid_ds结构体
函数执行成功返回0,失败返回-1
例如: 本程序完成了创建一个消息队列,并通过msgctl函数得到该消息队列的msqid_ds结构体信息,打印出来
#include<sys/msg.h>#include<stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#define SVMSG_MODE (0400|0200|0040|0004)
#define MAXSIZE 200
typedef struct my_msgbuf
{
long mtype;
char mchar[MAXSIZE];
}Message;
int main(int argc,char **argv)
{
int c,flags =SVMSG_MODE , mqid, key;
struct msqid_ds info;
while((c=getopt(argc,argv,"e"))!=-1) //若在命令行加上-e参数,则该flags = flags|=O_EXCL,否则flags不变
{ //即若在命令行加上-e参数,则指定已经创建过的消息队列,创建失败
switch(c)
{
case 'e':
flags|=IPC_EXCL;
break;
}
}
if(optind!=argc-1)
perror("usage:mscreate[-e]<name>");
key = ftok(argv[optind], 0); //返回键值
printf("key = %d\n", key);
mqid = msgget(key, flags|IPC_CREAT); //获取队列描述符
printf("mqid = %d\n", mqid);
if(mqid==-1)
printf("error:create MSG fail!\n");
msgctl(mqid, IPC_STAT, &info); //取出当前队列描述符的msqid_ds到info中
printf("mode = %o, cbytes = %lu, qnum = %lu, qtbyes= %lu\n",info.msg_perm.mode, (unsigned long )info.msg_cbytes, (unsigned long)info.msg_qnum, (unsigned long)info.msg_qbytes ); //打印当前信息
return 0;
}
运行结果:
例如: 服务器 、客户端程序,本程序实现了一个服务器多个客户端,使用类型为1表示任意客户发送到服务器的消息,服务器可接收,进行应答,该程序应答为读出客户请求文件到消息队列,客户端将自己的进程id作为发送给服务器的消息一部分,可以使服务器将该进程id作为消息类型,再将作出的应答发送,客户端通过类型接受自己的消息:
其中 客户到服务器 发送格式 类型 = 1, pid 路径名
服务器到客户端 发送格式 类型 = pid 文件内容
头文件 :mymsgbuf.h内容
#include<sys/msg.h>
#include<stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#define SVMSG_MODE (0400|0200|0040|0004)
#define MAXSIZE 200
typedef struct my_msgbuf / /定义自己的消息类型
{
long mtype;
char mchar[MAXSIZE];
}Message;
ssize_t mesg_recv (int id, Message *mptr) //调用msgrcv函数发送消息
{
ssize_t n;
n = msgrcv(id, &(mptr->mtype), MAXSIZE, mptr->mtype, 0);
return n;
}
ssize_t mesg_send(int id, Message *mptr, int n)//调用msgsnd接收消息
{
return (msgsnd(id, &(mptr->mtype), n, 0));
}
客户端程序:
#include "mymsgbuf.h"
void client (int , int );
int main(int argc, char ** argv)
{
int msqid;
msqid = msgget(ftok(".", 0), 0); //得到消息队列描述符
if(msqid == -1)
{
printf("msqid error");
exit(1);
}
client(msqid, msqid);
exit(0);
}
void client(int readfd, int writefd)
{;
char *ptr;
ssize_t n;
size_t len;
Message mesg;
snprintf(mesg.mchar, MAXSIZE, "%ld ", (long)getpid());
len = strlen(mesg.mchar);
ptr = mesg.mchar +len;
fgets(ptr, MAXSIZE-len, stdin);//输入要请求的文件
len = strlen(mesg.mchar);
if(mesg.mchar[len -1] == '\n')
len--;
mesg.mtype = 1; //将类型赋值为1,代表发送在消息队列中服务器可以接收该消息
mesg_send(writefd, &mesg, len); //发送到消息队列中
mesg.mtype = getpid();
while((n = mesg_recv(readfd, &mesg)) > 0)
{
write(1, mesg.mchar, n);
}
}
服务器端程序:
#include "mymsgbuf.h"
void server (int , int );
int main(int argc, char ** argv)
{
int msqid;
msqid = msgget(ftok(".", 0), 0666|IPC_CREAT);//创建消息队列
if(msqid == -1)
{
printf("msqid error");
exit(1);
}
server(msqid, msqid);
exit(0);
}
void server(int readfd, int writefd)
{
FILE *fp;
char *ptr;
pid_t pid;
ssize_t n;
Message mesg;
for(;;)
{
mesg.mtype = 1;
if ((n = mesg_recv(readfd, &mesg)) == 0) //接受消息类型为1的消息,即客户发送给服务器的消息
{
printf("mesg_recv error\n");
exit(1);
}
mesg.mchar[n] = 0;
if((ptr = strchr(mesg.mchar, ' ')) == NULL) //定位空格
{
printf("pathname :%s", mesg.mchar);
continue;
}
*ptr++ = 0;
printf("ptr = %s\n", ptr); //ptr为客户端请求打开的文件
pid = atol(mesg.mchar);//得到客户端的pid
mesg.mtype = pid;//指定消息类型为pid
if((fp = fopen(ptr, "r")) == NULL)//打开失败
{
snprintf(mesg.mchar+n, sizeof(mesg.mchar) - n, ":can't open, %s\n", strerror(errno));
n = strlen(ptr);
memmove(mesg.mchar, ptr, n);
mesg_send(writefd, &mesg, n );
}
else//打开成功,得到文件内容到mesg.mchar中,并发送到消息队列中
{
while(fgets(mesg.mchar, MAXSIZE, fp)!= NULL)
mesg_send(writefd, &mesg, strlen(mesg.mchar));
fclose(fp);
}
mesg_send(writefd, &mesg, 0); //发送一个0,表示结束
}
}