• 八、进程间通信消息队列


    相关链接消息队列

    一、概述

    1、什么是消息队列

      消息队列是进程间通信的一种,它是由操作系统维护的以字节序列为基本单位的间接通信机制,遵循先进先出的原则,它提供了一个进程向另一个进程发送一个带类型的数据块的方法。

    2、特点:

    • 消息队列是进程或线程间通讯的其中一种方式。遵循先进先出的原则,保证了时间的顺序性。拥有该消息队列读权限的进程可以从消息队列读出数据,拥有该消息队列写权限的进程可以向消息队列发送数据。

    • 消息作为节点一个一个地存放在消息队列里,可把消息队列比作信箱,消息比作依次顺序存放的信件。地址比作消息类型,内容为消息。

    • 支持双向传输,可以使用消息类型区分不同的消息。

    • 任何不同的进程都可以进行通讯不局限于父子进程的通讯。

    • 不足之处是消息的读写涉及数据拷贝,比较花费时间

    消息队列有两套机制:system v 和posix 前者移植性比较好,后者使用比较简单还可配合select机制使用、信号和线程通知。

    3、消息队列和有名管道的区别

    • 消息队列对每个数据都有一个最大长度的限制。

    • 消息队列也可以独立于发送和接收进程而存在,在进程终止时,消息队列及其内容并不会被删除

    • 管道只能承载无格式字节流,消息队列提供有格式的字节流,可以减少了开发人员的工作量

    • 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级,接收程序可以通过消息类型有选择地接收数据, 而不是像命名管道中那样,只能默认地接收

    • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的顺序接收,也可以按消息的类型接收。

    4、通过如下命令可以查看系统当前的IPC对象,没有使用的情况下可能为空

     

     

     

    二、消息队列的用法

    1、定义一个唯一key(ftok)

    key_t ftok(const char *pathname,int proj_id)  //获取一个key :将一个路径名和项目id转换成一个System V IPC key

    (1)参数:

    • pathname:一个合法路径(路径必须存在)。
    • proj_id:一个整数(不为0)。

    当使用相同的 proj_id 值时,命名同一文件的所有路径名的结果值相同。

    (2)返回值

    • 成功:返回合法键值
    • 失败: -1.

    2、构造消息对象(msgget)

    int msgget(key_t key,int msgflg)
    

    (1)参数

    • key:消息队列的键值。
    • msgflg:
      • IPC_CREAT: 如果消息队列不存在,则创建。
      • mode:访问权限。

    (2)返回值

    • 成功:消息队列的ID

    • 失败:-1

    3、发送特定类型消息(msgsnd)

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

    (1)参数

    • msqid:消息队列ID。
    • msgp:消息缓存区。
     struct msgbuf
        {
        ​	long mtype;	 //消息标识,必须大于0
        ​	char mtext[1]; //消息内容
        }

    (2)返回值  

    • 成功:0
    • 失败:-1

    4、接收特定类型消息(msgrcv)

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

    (1)参数

    • msqid:消息队列ID.

    • msgp:消息缓存区。

    • msgs:消息正文的字节数。

    • msgtyp:要接收消息的标识。

    • msgflagIPC_NOWAIT 非阻塞读取,MSG_NOERROR:截断消息。

    (2)返回值

    • 成功:0.
    • 失败:-1

    5、删除消息队列(msgctl)

    (1)函数原型:

      设置或获取消息队列的相关属性

     #include <sys/types.h>
     #include <sys/ipc.h>
     #include <sys/msg.h>
    int msgctl(int msgqid,int cmd,struct maqid_ds *buf)
    
    • msgqid:消息队列的ID
    • cmd:
      • IPC_STAT:获取消息队列的属性信息
      • IPC_SET:设置消息队列的属性
      • IPC_RMID:删除消息队列
    • buf:相关结构体缓冲区

     

               struct msqid_ds {
                   struct ipc_perm msg_perm;     /* Ownership and permissions */
                   time_t          msg_stime;    /* Time of last msgsnd(2) */
                   time_t          msg_rtime;    /* Time of last msgrcv(2) */
                   time_t          msg_ctime;    /* Time of last change */
                   unsigned long   __msg_cbytes; /* Current number of bytes in
                                                    queue (nonstandard) */
                   msgqnum_t       msg_qnum;     /* Current number of messages
                                                    in queue */
                   msglen_t        msg_qbytes;   /* Maximum number of bytes
                                                    allowed in queue */
                   pid_t           msg_lspid;    /* PID of last msgsnd(2) */
                   pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
               };
    

     

    三、实例

    使用消息队列实现两个进程的消息收发。

    1、 数据发送

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    
    #define BUFFER_SIZE 512
    
    struct message{
        long msg_type;
        char msg_text[BUFFER_SIZE];
    };
    
    void main(void)
    {
    
        int qid;
        key_t key;
        struct message msg;
    
        /*根据不同的路径和关键字产生标准的key*/
        if((key = ftok("/tmp",11)) == -1)
        {
           printf("ftok");
           exit(1); 
        }
        
        /*创建消息队列*/
        if((qid = msgget(key,IPC_CREAT|0666)) == -1)
        {
            printf("msgget");
            exit(1);
        }
        printf("Open queue %d\n",qid);
        
        while(1)
        {
            printf("Enter some message to the queue:");
            
            if((fgets(msg.msg_text,BUFFER_SIZE,stdin))==NULL)
            {
                puts("no message");
                exit(1);
            }
            msg.msg_type = getpid();
    
            /*添加消息到消息队列*/
            if((msgsnd(qid, &msg, strlen(msg.msg_text),0)) < 0)
            {
                printf("message posted");
                exit(1);
            }
    
            if(strncmp(msg.msg_text,"quit",4) == 0)
            {
                break;
            }
            
        
        }
    
        exit(0);
    }
    

    2、数据接收

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    
    #define BUFFER_SIZE 512
    
    struct message{
        long msg_type;
        char msg_text[BUFFER_SIZE];  //消息buffer
    };
    
    void main(void)
    {
    
        int qid;
        key_t key;
        struct message msg;
    
        /*根据不同的路径和关键字产生标准的key*/
        if((key = ftok("/tmp",11)) == -1)
        {
           printf("ftok");
           exit(1); 
        }
        
        /*创建消息队列*/
        if((qid = msgget(key,IPC_CREAT|0666)) == -1)
        {
            printf("msgget");
            exit(1);
        }
        printf("Open queue %d\n",qid);
        
        do{
            memset(msg.msg_text,0,sizeof(msg.msg_text));
              /*接收消息到消息队列*/
            if((msgrcv(qid, (void*)&msg, BUFFER_SIZE    ,0,0)) < 0)
            {
                printf("msgrcv");
               exit(1);
            }
            printf("The message form process %ld: %s",msg.msg_type,msg.msg_text);
        }while(strncmp(msg.msg_text,"quit",4)); //接收到quit则退出数据接收
    
        /*从系统内核中删除消息队列*/
        if((msgctl(qid,IPC_RMID,NULL)) < 0)
        {
            printf("msgctl");
            exit(1);
        }
        exit(0);
    } 

    执行结果:

     如果消息队列发送的数据长度大于接收设置的buffer大小,则消息队列会分两次进行接收。现在设置接收和发送的buffer都为10个字节,然后发送数据如下:

     

     

     

     

     

      

     

  • 相关阅读:
    服务器消息机制实现记录
    转载SQL经典代码按某一字段分组取最大(小)值所在行的数据
    记录js获取当前URL
    (原创)xilinx IP建立向导创建的目录和文件都是做什么的?由错误ERROR:HDLCompiler:Instantiating <xx> from unknown module <xx>引发的思考
    [转]NTFS3G的安装和配置
    (原创)Notepad++怎么实现双视图/双窗口?
    (原创)Quartus硬件工程路径改变,nios工程该怎么办?
    (原)verilog中的reg类型变量,一定会综合出触发器吗?
    (Windows)使用纯净版本的系统碟安装系统后没有网卡驱动怎么办?
    [转]NIOS_II的Boot过程分析
  • 原文地址:https://www.cnblogs.com/yuanqiangfei/p/16197734.html
Copyright © 2020-2023  润新知