消息队列

System V
随内核持续的IPC 对象数据结构
struct ipc_perm{
key_t key;
uid_t uid;
gid_t gid;
uid_t cuid;
gid_t cgid;
unsigned short mode;
unsigned short seq;
}
消息队列提供了一个从一个进程向另一个进程发送一块数据的方法,每个数据块都被认为是一个类型,接收者进程接受的数据块可以有
不同的类型值。
消息队列也有管道一样的不足,就是每个消息队列的最大长度是有上限的,每个消息队列的总的字节数是有上限的,系统上可以创建的
消息队列总数也有限制。

创建消息队列或者访问一个消息队列,成功返回一个非负整数,即消息队列的标识码;失败返回-1
int msgget(key_t key,it oflag)

shell中: ipcs命令查看 已存在的消息队列    ipcrm -q  msgid  删除一个消息队列

#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<sys/un.h>
#include<fcntl.h>
#include<sys/msg.h>
#define ERR_EXIT(m)
    do
    {
        perror(m);
        exit(EXIT_FAILURE);
    }while(0)
int main(void)
{
    int msgid;
    //与open函数类似
    msgid=msgget(1234,0666|IPC_CREAT);
    //msgid=msgget(1234,0666|IPC_CREAT|IPC_EXCL);
    //msgid=msgget(IPC_PRIVATE,0666|IPC_CREAT|IPC_EXCL);会创建一个新的消息队列,不能共享,只能用于本进程或者有亲缘关系进程之间通信
    //不指定选项也能成功
    //msgid=msgget(IPC_PRIVATE,0666);
    //msgid=msgget(1234,0400|IPC_CREAT);
    
    //msgid=msgget(1234,0600|IPC_CREAT);//无法打开
    if(msgid==-1)
        ERR_EXIT("msgget");
    printf("msgget succ
");
    return 0;

}

删除消息队列:

int msgctl(int msgid,int cmd,struct msqid_ds * buf)
cmd:
IPC_STAT:把msqid_ds结构中的数据设置为消息队列的当前关联值;
IPC_SET:在进程有足够的权限的前提下,把消息队列的当前关联值设置为msgid_ds数据结构中给出的值;
IPC_RMID:删除消息队列
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<sys/un.h>
#include<fcntl.h>
#include<sys/msg.h>
#define ERR_EXIT(m)
    do
    {
        perror(m);
        exit(EXIT_FAILURE);
    }while(0)
int main(void)
{
    int msgid;
    msgid=msgget(1234,0);
    if(msgid==-1)
        ERR_EXIT("msgget");
    msgctl(msgid,IPC_RMID,NULL);//删除消息队列,msgctl函数  int msgctl(int msgid,int cmd,struct msqid_ds * buf)
    return 0;
}

消息队列的数据结构
struct msqid_ds {
struct ipc_perm msg_perm; //IPC对象数据结构,每个IPC对象都有。
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_t msg_lrpid;
};

#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<sys/un.h>
#include<fcntl.h>
#include<sys/msg.h>
#define ERR_EXIT(m)
    do
    {
        perror(m);
        exit(EXIT_FAILURE);
    }while(0)
int main(void)
{
    int msgid;
    msgid=msgget(1234,0);
    if(msgid==-1)
        ERR_EXIT("msgget");
    printf("megget succ
");
    printf("msgid=%d
",msgid);
    
    struct msqid_ds  buf;
    msgctl(msgid,IPC_STAT,&buf);//获取消息队列状态信息
    printf("mode=%o
",buf.msg_perm.mode);//输出权限值
    //获取消息队列中当前字节数,当前消息总数,消息队列所能容纳的最大字节数
    printf("byte=%ld
number=%d
msgmnb=%d
",buf.__msg_cbytes,(int)buf.msg_qnum,(int)buf.msg_qbytes);
    sscanf("600","%o",(unsigned int *)&buf.msg_perm.mode);
    msgctl(msgid,IPC_SET,&buf);
    printf("mode=%o
",buf.msg_perm.mode);
    return 0;
}

消息队列中的每条消息是通过链表方式组织的,每条消息的最大长度不能超过MSGMAX.所有消息字节总和不能超过MSGMNB.系统中消息队列的
总数不能超过MSGMNI

把一条消息添加到消息队列中
int msgsnd(int msqid,const void*msgp,size_t msgsz,int msgflg);

第一个参数:由msgget函数返回的消息队列标识码;第二个参数:一个指针,指针指向准备发送的消息;第三个参数:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int

最后一个参数:msgfkg控制着当前消息队列满或者到达系统上限时将要发生的事:IPC_NOWAIT 表示队列满的时候不等待,而是返回EAGAIN错误。
struct msgbuf *msgp;
struct msgbuf {
long mtype;
char mtext[1];
};

消息大小不能超过MSGMAX;其次它必须以一个long int 长整数开始,接收者将利用这个long int 来确定消息类型。
先用该程序往消息队列中添加数据,再利用下一个程序到指定的消息队列中接收相应类型的消息

#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<sys/un.h>
#include<fcntl.h>
#include<sys/msg.h>
#define ERR_EXIT(m)
    do
    {
        perror(m);
        exit(EXIT_FAILURE);
    }while(0)
 struct msgbuf {
               long mtype;   //消息类型    
               char mtext[1];   
           };
int main(int argc,char * argv[])
{
    if(argc!=3)
    {
        fprintf(stderr,"Usage:%s <bytes> <type>
",argv[0]);
        exit(EXIT_FAILURE);
    }
    
    int len=atoi(argv[1]);//消息长度参数
    int type=atoi(argv[2]);//消息类型参数
    int msgid;
    msgid=msgget(1234,0);//打开key为1234的消息队列。必须先创建好消息队列
    if(msgid==-1)
        ERR_EXIT("msgget");
    struct msgbuf *ptr;//消息结构体指针
    ptr=(struct msgbuf *)malloc(sizeof(long)+len);
    ptr->mtype=type;
    //if(msgsnd(msgid,ptr,len,0)<0)
    //任意数据,不关心
    if(msgsnd(msgid,ptr,len,IPC_NOWAIT)<0)
        ERR_EXIT("msgsnd");
    return 0;
}

发送完消息后,可以运行第三个程序来查看消队列的状态。例如:./msg_send   200  2    发送200字节消息,类型是2

接受消息队列中的消息:

分析命令行参数其中短参数在getopt定义里分为三种:
  1. 不带值的参数,它的定义即是参数本身。
  2. 必须带值的参数,它的定义是在参数本身后面再加一个冒号。
  3. 可选值的参数,它的定义是在参数本身后面加两个冒号 。
  在这里拿上面的"1ac:d::"作为样例进行说明,其中的1,a就是不带值的参数,c是必须带值的参数,该参数的指针赋给optarg.d是可选值的参数。
  在实际调用中,'-1 -a -c cvalue -d', '-1 -a -c cvalue -ddvalue', '-1a -ddvalue -c cvalue'都是合法的。这里需要注意三点:
  1. 不带值的参数可以连写,象1和a是不带值的参数,它们可以-1 -a分开写,也可以-1a或-a1连写。
  2. 参数不分先后顺序,'-1a -c cvalue -ddvalue'和'-d -c cvalue -a1'的解析结果是一样的。
  3. 要注意可选值的参数的值与参数之间不能有空格,必须写成-ddvalue这样的格式,如果写成-d dvalue这样的格式就会解析错误。
getopt()每次调用会逐次返回命令行传入的参数。
  当没有参数的最后的一次调用时,getopt()将返回-1。
  当解析到一个不在optstring里面的参数,或者一个必选值参数不带值时,返回'?'。
  当optstring是以':'开头时,缺值参数的情况下会返回':',而不是'?' 。
int getopt(int argc,char * const argv[ ],const char * optstring);

接收消息,返回接收到的字节数。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
msgid:消息队列ID;
msgp:一个指针,指向准备接收的消息;
msgsz:是msgp指向的消息的长度,不包含类型long int 的长度;
msgtype:它可以实现接受优先级的简单形式.接收消息的类型,发送消息的程序中有消息的类型long int
  msgtype=0:返回消息队列第一条消息;
  msgtype>0:返回队列第一条类型=msgtype的消息;
  msgtype<0:返回队列类型小于等于msgtype绝对值的消息;

可以这么使用这个函数 ./msgrcv -n -t 2 -n表示非阻塞方式,队列为空的时候不阻塞;2是消息类型

#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<sys/un.h>
#include<fcntl.h>
#include<sys/msg.h>
#define ERR_EXIT(m)
    do
    {
        perror(m);
        exit(EXIT_FAILURE);
    }while(0)
#define MSGMAX 8192
 struct msgbuf {
               long mtype;       
               char mtext[1];   
           };
int main(int argc,char * argv[])
{
    int flag=0;
    int type=0;
    int opt;
    while(1)
    {
        opt=getopt(argc,argv,"nt:");
        if(opt=='?')
            exit(EXIT_FAILURE);//解析到了不认识的参数
        if(opt==-1)
            break;//所有参数解析完毕
        switch (opt)
        {
        case 'n':
            flag|=IPC_NOWAIT;
            break;
        case 't':
            type=atoi(optarg);// -t 2 表示还可以跟一个参数,这个参数存在optarg中
            break;
        default:
            break;
        }
    }

    int msgid;
    msgid=msgget(1234,0);
    if(msgid==-1)
        ERR_EXIT("msgget");
    struct msgbuf * ptr;
    ptr=(struct msgbuf *)malloc(sizeof(long)+MSGMAX);
    ptr->mtype=type;//要接受的消息的类型
    int n=0;
    if((n=msgrcv(msgid,ptr,MSGMAX,type,flag))<0)
        ERR_EXIT("msgsnd");
    printf("read %d bytes type=%ld
",n,ptr->mtype);
    return 0;
}
原文地址:https://www.cnblogs.com/wsw-seu/p/8555144.html