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

相关链接消息队列

一、概述

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个字节,然后发送数据如下:

 

 

 

 

 

 

  

 

posted @ 2022-04-27 09:53  轻轻的吻  阅读(271)  评论(0编辑  收藏  举报