Linux进程间通信——消息队列

一、前言

消息队列是一种以链表形式组织的数据结构,存放于内核中。消息队列通过唯一的IPC标识符表示。
相较于管道,消息队列更加灵活,它可以做到消息的类型的多样化。
它由如下特点:
消息队列发送的是一个自定义的数据块。
消息队列建立后可以独立于进程而存在。
可以根据消息类型有选择的接收信息。

二、常用函数

1、msgbuf

首先需要了解消息队列的模板
struct msgbuf
{
     long mtype;         /*type of message, must>0*/
     char mtext[1];      /*message text*/
};
数据结构msgbuf中有两个成员
mtype是指消息的类型,必须为长整型,必须是大于0。
mtext是消息数据,类型不仅仅限于字符型,也可以是其他任意类型,用户可以自己定义。

2、msgset

系统调用msgget()用于创建一个新的消息队列,或者存取一个已经存在的消息队列,其函数原型是:
int msgget(key_t key, int msgflg);
参数key是唯一标识消息队列的关键字,参数msgflg是一些标志,包括:

PC_CREAT:如果内核中没有此队列,则创建它。

IPC_EXCL:当和IPC_CREAT一起使用时,如果队列已经存在,则返回错误。

3、msgsnd

当由上面函数获得消息队列标识符,就可以发送消息了,msgsnd()用来向消息队列添加消息,原型为:
int msgsnd(int msqid, cosnt void *msgp, size_t msg_sz, int msgflg);
msgid是由msgget函数返回的消息队列标识符。
msgp是指向准备发送消息的指针,即以msgbuf为原型的数据结构
msg_sz是消息的长度,但不是整个结构体的长度,它不包括消息类型。 
一般可以表示为length = sizeof(struct mymsgbuf) - sizeof(long); 

msgflg可以设为:
0:此时为忽略此参数,如果消息队列已满,调用进程将会挂起,直到消息可以写入到队列中。

IPC_NOWAIT:如果消息队列己满,那么此消息则不会写入到消息队列中,控制将返回到调用进程中。消息队列写入成功时,函数返回0,否则返回-1。


4、msgrcv

用来从一个消息队列中获取消息,原型为:
int msgrcv(int msgid, void *msgp, size_t msg_sz, long int msgtype, int msgflg);
前三个参数msgid,msggp,msg_sz,和msgsnd中的含义相同。
msgtype是要从消息队列读取消息的类型
msgtype=0,接收消息队列的第一个消息,大于0则接收队列中消息类型等于这个值的第一个消息。小于0接收消息队列中小于或者等于msgtype绝对值的所有消息中的最小一个消息。一般为0。 
msgflg用于控制当队列中没有相应类型的消息可以接收时将发生的事情。

调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1.


5、msgctl

通过msgctl()可以对消息队列进行控制或者一些属性的修改,其函数原型为:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

第一个参数是消息队列的标识符,第二个参数cmd指定了操作,下面是几个常用的操作:

IPC_STAT:读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址中。

IPC_SET:设置消息队列的数据结构msqid_ds中的ipc_perm、msg_qbytes、msg_ctime元素的值。这个值取自buf参数。  

IPC_RMID:从系统内核中移走消息队列。


消息队列标识符的属性被记录在一个msgid_ds结构体: 

struct msqid_ds{
  struct ipc_perm msg_perm;      /*所有者和权限*/
  time_t          msg_stime;     /*最后一次向队列发送消息的时间*/
  time_t          msg_rtime;     /*最后一次从队列接收消息的时间*/
  time_t          msg_ctime;     /*队列最后一次改动的时问*/
  unsigned long   __msg_cbytes;  /*当前队列所有消息的总长度*/
  msgqnum_t       msg_qnum;      /*当前队列中的消息数量*/
  msglen_t        msg_qbytes;    /*消息队列的最大消息总长度*/
  pid_t           msg_lspid;     /*最一次给队列发送消息的进程PID*/
  pid_t           msg_lrpid;     /*最后一次从队列接收消息的进程PID*/
}


三、消息队列实现进程间通信

发送端源程序
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <errno.h>
#include <iostream>
using namespace std;

const int MAX_SEND_SIZE = 1024;

struct msgbuf_m
{
    long int mtype;
    char mtext[MAX_SEND_SIZE];
    msgbuf_m()
    {
        memset(mtext,0,sizeof(mtext));
    }

    int size()
    {
        return strlen(mtext)+1;
    }
};

int main()
{
    int msg_id = msgget((key_t)1111,0666 | IPC_CREAT);
    if(msg_id == -1)
    {
        cerr << "msgget failed with error: " << errno << endl;
        exit(1);
    }

    msgbuf_m data;
    char buf[MAX_SEND_SIZE];
    while(1)
    {
        cout << ">";
        memset(buf,0,sizeof(buf));
        cin.getline(buf,MAX_SEND_SIZE);
        data.mtype = 1;
        strcpy(data.mtext,buf);

        if(msgsnd(msg_id,(void *)&data,data.size(),0) == -1)
        {
            cerr << "msgsnd error" << endl;
            exit(1);
        }

        if(strncmp(buf,"quit",4)==0)
            break;
    }
    return 0;
}


接收端源程序
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <errno.h>
#include <iostream>
using namespace std;

const int MAX_RECV_SIZE = 1024;

struct msgbuf_m
{
    long int mtype;
    char mtext[MAX_RECV_SIZE];
};

int main()
{
    int msg_id = msgget((key_t)1111,0666 | IPC_CREAT);
    if(msg_id == -1)
    {
        cerr << "msgget failed with error: " << errno << endl;
        exit(1);
    }

    msgbuf_m data;
    while(1)
    {
        if(msgrcv(msg_id,(void *)&data,MAX_RECV_SIZE,0,0) == -1)
        {
            cerr << "msgrcv error" << endl;
            exit(1);
        }

        cout << "receive data: " << data.mtext << endl;

        if(strncmp(data.mtext,"quit",4) == 0)
            break;
    }

    if(msgctl(msg_id,IPC_RMID,0) == -1)
    {
        cerr << "msgctl remove failed" << endl;
        exit(1);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值