System V消息队列

消息队列

1.消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法

2.每个数据块都被认为是有一个类型,称为消息,消息与消息之间是有边界的(管道的字节流是无边界的)接收者进程接收的数据块可以有不同的类型值

3.消息队列也有管道一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是由上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)

IPC对象数据结构

内核为每个IPC对象维护一个数据结构
struct ipc_perm {
	key_t          __key;       /* Key supplied to xxxget(2) */
	uid_t          uid;         /* Effective UID of owner */
	gid_t          gid;         /* Effective GID of owner */
	uid_t          cuid;        /* Effective UID of creator */
	gid_t          cgid;        /* Effective GID of creator */
	unsigned short mode;        //访问权限
	unsigned short __seq;       //序号
};
不管是消息队列、共享内存、信号量,都具有上述的数据结构

消息队列结构

struct msqid_ds {
	struct ipc_perm msg_perm;     //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;      //最后一个从消息队列中接收数据的进程号
};

消息队列在内核中的表示

消息队列函数

msgget函数

功能:

用来创建和访问一个消息队列

原型:

int msgget(key_t key,int msgflg);

参数:

    key:某个消息队列的名字

    msgflg:由9个权限标志构成,它们的用法和创建文件时使用的mode模式标志一样

返回值:

成功返回一个非负整数,即该消息队列的标识码;失败返回-1

应用代码:
int main(void)
{
        int msgid;
        //如果消息队列已经存在,则是打开
        msgid=msgget(1234,0666 | IPC_CREAT);
        //只要指定IPC_PRIVATE,就创建一个新的消息队列
        //IPC_PRIVATE创建出来的队列,是不能被其它进程共享的,因为其它进程也用IPC_PRIVATE的话,会创建一个不同的IPC队列出来
        //msgid=msgget(IPC_PRIVATE,0666 | IPC_CREAT | IPC_EXCL);
        if(msgid==-1)
                ERR_EXIT("msgget");
        printf("msgget succ\n");

        return 0;
}

说明:


msgctl函数

功能:

消息队列的控制函数

原型:

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

参数:

    msqid:由msgget函数返回的消息队列标识码

    cmd:将要采取的动作(有三个可取值)


    buff:动作需要传递的参数

返回值:

成功返回0,失败返回-1

IPC_RMID使用代码
//删除消息队列
int main(void)
{
        int msgid;
    
        //先打开消息队列
        msgid=msgget(1234,0);
        if(msgid==-1)
                ERR_EXIT("msgget");
        printf("msgget succ\n");
        printf("msgid=%d\n",msgid);
    
        //从系统中删除由msgid指定的消息队列
        msgctl(msgid,IPC_RMID,NULL);
        return 0;
}
IPC_SET使用代码
//设置消息队列状态
//把权限666改为600
int main(void)
{
        int msgid;
    
        //先打开消息队列
        msgid=msgget(1234,0);
        if(msgid==-1)
                ERR_EXIT("msgget");
        printf("msgget succ\n");
        printf("msgid=%d\n",msgid);
    
        struct msqid_ds buf;
        msgctl(msgid,IPC_STAT,&buf);
        sscanf("600","%o",(unsigned int*)&buf.msg_perm.mode);
        msgctl(msgid,IPC_SET,&buf);
        return 0;
}
IPC_STAT使用代码
//查看消息队列的状态
int main(void)
{
        int msgid;
    
        //先打开消息队列
        msgid=msgget(1234,0);
        if(msgid==-1)
                ERR_EXIT("msgget");
        printf("msgget succ\n");
        printf("msgid=%d\n",msgid);
    
        struct msqid_ds buf;
        msgctl(msgid,IPC_STAT,&buf);
        //IPC对象的存取权限
        printf("mode=%o\n",buf.msg_perm.mode);
        //当前队列中消息的字节数
        printf("bytes=%ld\n",buf.msg_cbytes);
        //当前队列中消息的个数
        printf("number=%d\n",(int)buf.msg_qnum);
        //队列中允许存放的最大字节数
        printf("msgmax=%d\n",(int)buf.msg_qbytes);
        return 0;
}

msgsnd函数

功能:

把一条消息添加到消息队列中

原型:

int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);

参数:

    msgid:由msgget函数返回的消息队列标识码

    msgp:是一个指针,指针指向准备发送的信息

    msgsz:是msgp指向的消息长度,这个长度不包含保存消息类型的那个long int长整型

    msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情

        msgflg=IPC_NOWAIT表示队列满不等待,返回EAGAIN错误

返回值:

成功返回0,失败-1

说明:

消息结构在两方面收到制约。首先,它必须小于系统规定的上限值;其次,它必须以一个long int长整数开始,接收者函数将利用这个长整型确定消息的类型

消息结构参考形式如下:

struct msgbuf
{
    long mtype;//消息的类型
    char mtext[1];//存放消息的场所
}
应用代码
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        }while(0)

//消息结构参考格式
struct msgbuf
{
        long mtype;
        char mtext[1];
};

/*
argc表示main函数的参数个数
argv表示传入main函数的参数序列或指针,并且第一个参数argv[0]一定是
程序的名称,并且包含了程序所在的完整路径
*/
//两个参数,消息长度和消息类型
int main(int argc,char *argv[])
{
        if(argc!=3)
        {
                fprintf(stderr,"Usage:%s <bytes> <type>\n",argv[0]);
                exit(EXIT_FAILURE);
        }
        //保存消息长度
        //atoi是把字符串转换成整形数的一个函数
        int len=atoi(argv[1]);
        //保存消息类型
        int type=atoi(argv[2]);
        int msgid;
    
        //先打开消息队列,要往消息队列中发送数据
        msgid=msgget(1234,0);
        if(msgid==-1)
                ERR_EXIT("msgget");

        struct msgbuf *ptr;
        //分配的字节数,len是消息的长度
        ptr=(struct msgbuf*)malloc(sizeof(long)+len);
        ptr->mtype=type;
        //0表示当消息队列满的时候,以阻塞的方式发送
        //if(msgsnd(msgid,ptr,len,IPC_NOWAIT)<0)则不会阻塞,返回一个错误
        if(msgsnd(msgid,ptr,len,0)<0)
                ERR_EXIT("msgsnd");
        return 0;
}

msgrcv函数

功能:

从一个消息队列接收信息

原型:

ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtype,int msgflg);

参数:

    msgid:由msgget函数返回的消息队列标识码

    msgp:是一个指针,指向准备接收的消息

    msgsz:是msgp指向的消息长度,这个长度不包含保存消息类型的那个long int长整型

    msgtype:可以实现接收优先级的简单形式

        msgtype=0返回队列第一条消息

        msgtype>0返回队列第一条类型等于msgtype的消息

        msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息

    msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事

        msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误

        msgflg=MSS_NOERROR,消息大小超过msgsz时被截断

        msgflg>0且msgflg=MSC_EXCEPT,接收类型不等于msgtype的第一条消息

返回值:

成功返回实际放到接收缓冲区去的字符个数,失败返回-1

应用代码
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.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)
        {
                /*
                getopt()用来分析命令行参数,参数argc和argv代表参数个数
                和内容,跟main()函数参数一样,参数optstring为选项字符串
                */
                opt=getopt(argc,argv,"nt:");
                //表示解析到了不认识的参数
                if(opt=='?')
                        exit(EXIT_FAILURE);
                if(opt==-1)
                        break;
                }
        }
        int msgid;
        msgid=msgget(1234,0);
        if(msgid==-1)
                ERR_EXIT("msgget");

        struct msgbuf *ptr;
        //消息的最大值不能超过8192
        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",n,ptr->mtype);
        return 0;
}


猜你喜欢

转载自blog.csdn.net/wk_bjut_edu_cn/article/details/80500860