Linux中的消息队列

来源:互联网 发布:惠州网络问政阳光清单 编辑:程序博客网 时间:2024/06/06 01:57

⼀、什么是消息队列
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。我们可以通过发送
消息来避免管道的同步和阻塞问题。消息与管道不同的是,消息队列是基于消息的,而管
道是基于字节流的。消息队列的读取不一定是先进先出的。
消息队列的不足是:
1. 每个消息的最大长度是有限的(MSGMAX)
2. 每个消息的总的字节数是有限的(MSGMNB)
3. 系统上消息队列的总数也有一个上限(MSGMNI)
这里写图片描述
二、IPC对象数据结构
内核为每个IPC对象维护一个数据结构(/usr/include/linux/ipc.h)
这里写图片描述
消息队列、共享内存和信号量都有这样一个共同的数据结构
三、消息队列数据结构(/usr/include/linux/msg.h)
这里写图片描述
可以看到第一个条目就是IPC结构体,即是公有的,后面的都是消息队列所私有的成员,
消息队列是用链表实现的
四、函数
1. 创建新消息队列或取得已存在的消息队列
原型: int msgget(key_t key,int msgflag)
参数:
key可以认为是一个端口号也可以由ftok函数生成
msgflag:
IPC_CREAT 如果IPC不存在,则创建一个IPC资源,否则打开操作;

IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建⽴,否则就产⽣错误。

如果将IPC_CREAT 和I PC_EXCL标志⼀起使⽤,XXXget()将返回⼀个新建的IPC标识符;
如果该IPC资源已存在,或者返回-1。
IPC_EXEL标志本⾝并没有太⼤的意义,但是和IPC_CREAT标志⼀起使⽤可以⽤来保证
所得的对象是新建的,⽽不是打开已有的对象。
2. 向队列中读写消息
原型:
int msgsnd( int msqid, const void* msgp, size_t msgsz, int msgflag )
int msgrcv( int msqid, const void* msgp, size_t msgsz, long msgtyp, int msgflag )
参数:
msqid消息队列的标识码;

msgp:指向消息缓冲区的指针,此位置⽤来暂时存储发送和接收的消息,是⼀个⽤户可
定义的通⽤结构,形态如下:
struct msgstru{
long mtype; //⼤于0
char mtext[⽤户指定⼤⼩];
};

msgsz:消息的大小;

msgtyp:从消息队列中读取的消息形态。如果值为0,则表示消息队列中的所有消息都会被读取;

msgflg:⽤来指明核⼼程序在队列没有数据的情况下所应采取的⾏动。如果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操作;系统定义了三种cmd操作
IPC_STAT:该命令用来获取消息队列对应的msqid_ds数据结构,并将
其保存到buf指定的地址空间
IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf中。
IPC_RMID:从内核中删除msqid标识的消息队列

我们可以定义一个comm.h存放头文件 和函数接口…
comm.c产生消息队列
server.c和client.c两个不同的进程

//comm.h#ifndef __COMM_H__#define __COMM_H__#include<stdio.h>#include<string.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#define PATHNAME "."#define PROJ_ID 0x6666#define SERVER_TYPE 1#define CLIENT_TYPE 2struct msgbuf{    long mtype;    char mtext[1024];};int creatMsgQueue();  //创建消息队列int getMsgQueue();   //获取消息队列int sendMsg(int msgid,int who,char* msg);  //发送消息int recvMsg(int msgid,int type,char out[]); //接受消息int DestoryMsgQueue(int msgid);  //删除消息队列#endif 
//comm.c#include "comm.h"static int comMsgQueue(int msg){    key_t key=ftok(PATHNAME,PROJ_ID);    if(key<0)    {        perror("ftok");        return -1;    }    int msgid=msgget(key,msg);    if(msgid<0)    {        perror("msgget");        return -2;    }    return msgid;}int creatMsgQueue(){    return comMsgQueue(IPC_CREAT|IPC_EXCL|0666);}int getMsgQueue(){    return comMsgQueue(IPC_CREAT);}int sendMsg(int msgid,int who,char* msg){    struct msgbuf buf;    buf.mtype=who;    strcpy(buf.mtext,msg);    if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0)<0)    {        perror("msgsnd");        return -4;    }    return 0;}int recvMsg(int msgid,int type,char out[]){    struct msgbuf buf;    if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),type,0)<0)    {        perror("msgrcv");        return -5;    }        strcpy(out,buf.mtext);        return 0;}int DestoryMsgQueue(int msgid){    if(msgctl(msgid,IPC_RMID,NULL)<0)    {        perror("msgctl");        return -3;    }}
//server.c#include"comm.h"int main(){    int msgid=creatMsgQueue();    char buf[1024];    while(1)    {        recvMsg(msgid,CLIENT_TYPE,buf);        printf("client say#:%s\n",buf);        printf("Please Enter#:");        fflush(stdout);        ssize_t s=read(0,buf,sizeof(buf)-1);        if(s>0)        {            buf[s]=0;        }        sendMsg(msgid,SERVER_TYPE,buf);    }    DestoryMsgQueue(msgid);    return 0;}
//client.c#include"comm.h"int main(){    int msgid=getMsgQueue();    char buf[1024];    while(1)    {        buf[0]=0;        printf("Please Enter#:");        fflush(stdout);        ssize_t s=read(0,buf,sizeof(buf)-1);        if(s>0)        {            buf[s]=0;            sendMsg(msgid,CLIENT_TYPE,buf);        }        recvMsg(msgid,SERVER_TYPE,buf);        printf("server say#:%s\n",buf);    }    return 0;}
//Makefile.PHONY: allall:server clientserver: server.c comm.c    gcc -o $@ $^client:client.c comm.c    gcc -o $@ $^.PHONY: cleanclean:    rm -f client server
  1. ipcs -q //查看系统中的消息队列
  2. ipcrm - q //删除系统中的消息

这里写图片描述

编写一个监视系统消息队列的代码;

 while :; do ipcs -q; sleep 1; echo "####################################"; done

测试两个不同进程通过消息队列进行通信:
打开两个终端:
让client.c先发消息;server.c先收消息

这里写图片描述
两个进程之间通过消息队列进行通信的优点:可以是两个任意的进程,即使它们没有血缘关系。

原创粉丝点击