• 消息队列


    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;
    }
  • 相关阅读:
    【Python】批量导出word文档中的图片、嵌入式文件
    关于loguru日志模板并发重复打印修复
    wordpress检索分类法函数:get_terms
    java函数式编程及Consumer、Supplier、Function、Predicate四大接口
    解决 centos7 虚拟机 github 访问太慢的问题
    error execution phase preflight: couldn't validate the identity of the API Server.. x509: certificate has expired or is not yet valid:...
    vscode 避免每次ssh都输入密码
    深入理解 docker build 中的构建上下文
    SSH 出现 The authenticity of host xxx can't be established.
    Viusal Studio 代码片段管理
  • 原文地址:https://www.cnblogs.com/wsw-seu/p/8555144.html
Copyright © 2020-2023  润新知