进程间通信-消息队列

来源:互联网 发布:淘宝请人装修要多少钱 编辑:程序博客网 时间:2024/05/16 15:25

前言

有三种称作XSI IPC的IPC:消息队列、信号量以及 共享内存。它们只见有很多的相似之处。
消息队列是消息的链接表,存储在内核中,由消息队列标识符表示。它不同于管道,其生命周期是随内核的。

一、什么是消息队列

消息队列提供了⼀一种从⼀一个进程向另⼀一个进程发送⼀一个数据块的⽅方法。 每个数据块都被认为是有⼀一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。Linux用宏MSGMAX和MSGMNB来限制一条消息的最大长度和一个队列的最大长度。

二、如何使用消息队列

1.创建一个新队列或打开一个现有队列

#include<sys/msg.h>int msgget(key_t key,int flag);

参数:
key:可以认为是⼀一个端⼜⼝口号,也可以由函数ftok⽣生成。

msgflg:
IPC_CREAT 如果IPC不存在,则创建⼀一个IPC资源,否则打开操作。
IPC_EXCL:只有在共享内存不存在的时候,新的共享内存才建⽴立,否则就产⽣生错误。
如果单独使⽤用IPC_CREAT,xxxget()函数要么返回⼀一个已经存在的共享内存的操作符,要么返回⼀一个新建的共享内存的标识符。
如果将IPC_CREAT和IPC_EXCL标志⼀一起使⽤用,XXXget()将返回⼀一个新建的IPC标识符;如果该IPC资源已存在,或者返回-1。
IPC_EXEL标志本⾝身并没有太⼤大的意义,但是和IPC_CREAT标志⼀一起使⽤用可以⽤用来保证所得的对象是新建的,⽽而不是打开已有的对象。

2.向队列读/写消息

原型:
①msgrcv从队列中取⽤用消息
②msgsnd将数据放到消息队列中

#include<sys/msg.h>ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数:
msqid:消息队列的标识码
msgp:指向消息缓冲区的指针,此位置⽤用来暂时存储发送和接收的消息,是⼀一个⽤用户可定义的通⽤用结构,形态如下:

//可自定义通用结构体struct msgstru{    long mtype; //⼤大于0    char mtext[⽤用户指定⼤大⼩小];};

注意:第二个参数是指向一个结构体的一个指针,这个结构体叫做struct msgbuf这个结构体,第一个变量是消息的类型,第二个是存放消息的数组。

参数:
msgsz:消息的⼤小。
msgtyp:从消息队列内读取的消息形态。如果值为零,则表⽰示消息队列中的所有消息都会被读取。
msgflg:⽤用来指明核⼼心程序在队列没有数据的情况下所应采取的⾏行动。有两种方式:0 和IPC_NOWAIT

如果msgflg和常数IPC_NOWAIT合⽤用,则在msgsnd()执⾏行时若是消息队列已满,则msgsnd()将不会阻塞,而会⽴立即返回-1。
如果执⾏行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。

当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。

3.设置消息队列属性

原型:

int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );

参数:msgctl 系统调⽤用对 msgqid 标识的消息队列执⾏行 cmd 操作,系统定义了 3 种 cmd 操作: IPC_STAT , IPC_SET , IPC_RMID

IPC_STAT : 该命令⽤用来获取消息队列对应的 msqid_ds 数据结构,并将其保存到 buf 指定的地址空间

IPC_SET : 该命令⽤用来设置消息队列的属性,要设置的属性存储在buf中
IPC_RMID : 从内核中删除 msqid 标识的消息队列

common.h

#ifndef _COMMON_H_#define _COMMON_H_#include<stdlib.h>#include<stdio.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#include<time.h>#include<string.h>#define PERM 0666#define _MSG_SIZE_ 1024#define PATHNAME "./"#define PROJ_ID 0x6666#define MYSIZE 128#define SERVER_TYPE 1#define CLIENT_TYPE 2struct msgbuf{    long mtype;    char mtext[MYSIZE];};int CommMsg(int msgflag);int getmsg();int CreateMsgQueue();int SendMsg(int msgid,long type,const char* msg);int RecvMsg(int msgid, int type,char out[]);int DestroyMsgQueue(int msgid);#endif

common.c

#include"common.h"int CommMsg(int msgflag){    key_t _key=ftok(PATHNAME,PROJ_ID);    if(_key<0)    {    perror("ftok");    }    int msgid=0;    if(msgid=msgget(_key,msgflag)<0)    {        perror("msgget");        return 1;    }    return msgid;}int CreateMsgQueue(){    return CommMsg(IPC_CREAT|IPC_EXCL|PERM);}int getmsg(){    return CommMsg(0);}int SendMsg(int msgid,long type,const char* msg){    struct msgbuf buf;    buf.mtype=type;    strcpy(buf.mtext,msg);    int id=msgsnd(msgid,&buf,sizeof(buf.mtext),0);    if(id<0)    {        perror("msgsnd");        return -1;    }    return 0;}int RecvMsg(int msgid,int type,char out[]){    struct msgbuf buf;    int size=msgrcv(msgid,&buf,sizeof(buf.mtext),type,0);    if(size>0)    {        strncpy(out,buf.mtext,size);    return 0;    }    perror("msgrcv");    return -1;}int DestroyMsgQueue(int msgid){    if(msgctl(msgid,IPC_RMID,NULL)<0)    {        perror("msgctl");        return -1;    }    return 0;}

serve.c

#include"common.h"int main(){    int msg=CreateMsgQueue();    char buf[2*MYSIZE];    int msgid=getmsg();    printf("serve is running!");    while(1)    {        if(RecvMsg(msgid,CLIENT_TYPE,buf)<0)        {        break;        }        printf("client# %s\n",buf);        if(SendMsg(msgid,SERVER_TYPE,buf)<0)        {        break;        }    }    DestroyMsgQueue(msgid);    return 0;}

client.c

#include"common.h"int main(){    int msgid=getmsg();    char buf[MYSIZE];    char out[2*MYSIZE];    while(1)    {        printf("please input:");        fflush(stdout);        ssize_t _s=read(0,buf,sizeof(buf)-1);        if(_s>0)        {            buf[_s]='\0';            SendMsg(msgid,CLIENT_TYPE,buf);        }        if(RecvMsg(msgid,SERVER_TYPE,out)<0)        {            break;        }        printf("serve echo :%s\n",out);    }    return 0;}

Makefile

.PHONY: allall: client serveserve:serve.c common.c    gcc -o $@ $^client: client.c common.c    gcc -o $@ $^.PHONY:cleanclean:    rm -f serve client 

这里写图片描述

另外,学习两条命令:
ipcs -q (查看消息队列)
ipcrm -q msgid(删除指定消息队列)

这里写图片描述