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

12 篇文章 3 订阅
4 篇文章 0 订阅


前言

消息队列是进程间通信的最主要方法之一,相比于其他方法而言,信号队列成功克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。在多进程之间需要协同处理某个任务时能够合理的进行进程间的同步以及数据交流。

以下是本篇文章正文内容,下面案例可供参考

一、什么是消息队列?

消息队列(Message Queue,简称MQ)是由内核管理的消息链接表,由消息队列标识符标识,标识符简称队列ID。消息队列提供了进程之间单向传送数据的方法,每个消息包含有一个正的长整型类型的数据段、一个非负的长度以及实际数据字节数(对应于长度),消息队列总字节数是有上限的,系统上消息队列总数也有上限。

MQ传递的是消息,也就是进程间需要传递的数据,系统内核中有很多MQ,这些MQ采用链表实现并由系统内核维护,每个MQ用消息队列描述符(qid)来区分,每个MQ 的pid具有唯一性。如下图:在进程间通信时,一个进程A将消息加到由内核管理的MQ 末端,另一个进程B在MQ中获取消息(获取信息时不遵循先进先出的规则,也可以按照消息类型字段获取消息):

在这里插入图片描述

二、消息队列使用的函数

消息队列(MQ)在进程间通信的操作分为以下四个步骤:
1、创建MQ;
2、发送message;
3、接收message;
4、删除MQ.
所需函数如下:

1、ftok () 函数

  key_t ftok(const char **pathname,int proj_id);

该函数是根据pathname指定的文件或目录的索引节点号和proj_id计算并返回一个key_t类型的ID值,失败则返回-1;
第一个参数pathname是一个系统中必须存在的文件或文件夹的路径,会使用该文件的索引节点;
第二个参数prij_id是用户指定的一个子序号,这个数字有的称之为project ID,它是一个8bit的整数,取值范围为1~255.
注意:如果要确保key值不变,要么确保fork()的文件不会被删除,要么不用fork()指定一个固定的key值。

2、msgget () 函数

功能:创建一个新队列或者打开一个现有队列,成功返回一个非负整数,即该共享内存段的标识码,失败返回-1;
函数原型:

   #include<sys/msg.h>
   
   int msgget(key_t key,int msgflg);

说明:
key是fork()返回的key_t类型键值;
msgflg是创建标志:IPC_CREAT,不存在则创建,存在则返回已有的mqid,IPC_CREAT|IPC_EXCL,不存在则创建,存在则返回出错。

3、msgsnd () 函数

功能:将新消息添加到队列尾端,也就是发送一条消息,必须要有写消息队列的权限。成功返回0;失败返回-1;并设置errno.
函数原型:

  #include<sys/msg.h>
  
   int msgsnd(int msqid,const void *ptr,size_t msgsz,int magflg);

说明:
msgid 是由msgget函数返回的消息队列 ID;
ptr是一个指针,它指向要发送的消息结构体类型的变量,消息结构在两方面受到制约。首先,它必须小于系统规定的上限值;其次,它必须以一个long int 长整数开始,接收者函数将利用这个长整数确定消息的类型,其参考类型定义如下:

    typedef struct s_msgbuf
   {
        long    mtype;                /*positive message type */
        char    mtext[512];         /*message data,of  length nbytes */
   }t_msgbuf;

msgsz是要发送消息的长度;
msgflg控制着当前消息队列已满或系统上限时将要发生的事情,设置为IPC_NOWAIT表示队列已满不等待,返回EAGAIN错误。

4、msgrcv () 函数

功能:用于从队列中取消息,成功返回实际放到接收缓冲区里去的个数,失败返回-1;并设置errno.
函数原型:

#include<sys/msg.h>

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

参数说明:
msgid是由msgget 函数返回的消息队列ID;
ptr是一个指针,它指向准备接收的消息;
msgsz是msgp指向的消息长度,这个长度不含保存消息类型的long int 长整型;
msgtype是消息的类型,它可以指定想要哪一种消息,可以实现接收优先级的简单形式;;
type=0 返回队列中的第一个值;
type>0 返回队列中消息类型为type的第一个消息;
type<0 返回队列中消息类型值≤type绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。
type 值非0 用于以非先进先出次序读取消息,程序若给消息赋予优先权,则type就可以是优先权值,如果一个消息队列由多个客户进程和一个服务器进程使用,则type字段可以用来包含客户进程的进程ID(前提是ID可以存放在长整型中)。

5、 msgctl () 函数

功能:msgctl函数对队列执行多种操作,用于控制消息队列。它和进程间通信的另外两种方法(信号量和共享存储)中的函数(semctl和shmctl)都是类似于ioctl函数(也叫垃圾桶函数)。
函数原型:

#include<sys/msg.h>

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

msgid:msgget 函数返回的消息队列ID;
cmd:该参数指定对msqid指定的队列要执行的命令,可取一下三个值:
IPC_STAT:取msqid_ds结构,并将它存放在buf 指向的结构中;
IPC_SET:将字段msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes从buf指向的结构复制到与 这个队列相关的msqid_ds结构中;
IPC_RMID:从系统中删除该消息队列以及仍在该队列中的所有数据。

三、信息在进程之间传递

1、进程A往内核的消息队列中写入内容“ping”

#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<stdlib.h>

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

#define FTOK_PATH        "/dev/zero"
#define FTOK_PROJID      0x22

typedef struct s_msgbuf
{
        long    mtype;
        char    mtext[1024];
}t_msgbuf;

int main (int argc,char **argv)
{
        key_t           key;
        int             msgid;
        t_msgbuf        msgbuf;
        int             msgtype;
        int             i;

        if((key=ftok(FTOK_PATH,FTOK_PROJID)) < 0)
        {
                printf("ftok() get IPC token failure: %s\n",strerror(errno));
                return -1;
        }
    
        msgid = msgget(key,IPC_CREAT|0666);
        if( msgid < 0 )
        {
         printf("shmget() create shared memory failure: %s\n",strerror(errno));
                return -2;
        }

        msgtype = msgtype = (int)key;
        printf("key[%d] msgid[%d] msgtype[%d\n]",(int)key,msgid,msgtype);

        for(i=0;i<4;i++)
        {
                memset(&msgbuf,0,sizeof(msgbuf));
                if(msgrcv(msgid,&msgbuf,sizeof(msgbuf.mtext),msgtype,IPC_NOWAIT) < 0)
                {
                        printf("msgsnd() receive message failure: %s\n",strerror(errno));
                        break;
                }
                printf("Receive message: %s\n",msgbuf.mtext);

                sleep(1);
        }

        msgctl(msgid,IPC_RMID,NULL);

        return 0;

}

2、进程B从内核的消息队列中读取内容“ping”

#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<stdlib.h>

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

#define FTOK_PATH        "/dev/zero"
#define FTOK_PROJID      0x22

typedef struct s_msgbuf
{
        long    mtype;
        char    mtext[1024];
}t_msgbuf;
         
int main (int argc,char **argv)
{        
        key_t           key;
        int             msgid;
        t_msgbuf        msgbuf;
        int             msgtype;
        int             i;

        if((key=ftok(FTOK_PATH,FTOK_PROJID)) < 0)
        {
                printf("ftok() get IPC token failure: %s\n",strerror(errno));
                return -1;
        }
    
        msgid = msgget(key,IPC_CREAT|0666);
        if( msgid < 0 )
         {
                printf("ftok() get IPC token failure: %s\n",strerror(errno));
                return -1;
        }
    
        msgid = msgget(key,IPC_CREAT|0666);
        if( msgid < 0 )
        {
                printf("shmget() create shared memory failure: %s\n",strerror(errno));
                return -2;
        }

        msgtype = msgtype = (int)key;
        printf("key[%d] msgid[%d] msgtype[%d\n]",(int)key,msgid,msgtype);

        for(i=0;i<4;i++)
        {
                memset(&msgbuf,0,sizeof(msgbuf));
                if(msgrcv(msgid,&msgbuf,sizeof(msgbuf.mtext),msgtype,IPC_NOWAIT) < 0)
                {
                        printf("msgsnd() receive message failure: %s\n",strerror(errno));
                        break;
                }
                printf("Receive message: %s\n",msgbuf.mtext);

                sleep(1);
        }

        msgctl(msgid,IPC_RMID,NULL);

        return 0;

}

运行结果如下:
进程A向内核的消息队列写入“ping”:
在这里插入图片描述
进程B从内核的消息队列读取到“ping”:
在这里插入图片描述
总结:内核为每个IPC对象维护了一个数据结构struct ipc_perm,来标识消息队列,方便进程知道哪一个队列正在被操作,每一个msqid_ds表示一个消息队列。并通过msqid_ds.msg_first、msg_last维护一个先进先出的链表队列,当发送一个消息到该队列时,把发送的消息构造成一个msg的结构对象,并添加到msqid_ds.ms_first、msg_last维护的链表队列。
以上就是今天要讲的内容,本文仅仅简单介绍了进程间通信的方法之一:消息列表,进程间通信的方法还有很多种,下一篇博文我们将讲解另外一种方法:共享内存。
恭请指正!

  • 10
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mierya0707

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值