前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UNIX(进程间通信):10 消息队列

UNIX(进程间通信):10 消息队列

作者头像
用户3479834
发布2021-03-04 14:40:18
9190
发布2021-03-04 14:40:18
举报
文章被收录于专栏:游戏开发司机游戏开发司机

一、消息队列的特点

1.消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识. 2.消息队列允许一个或多个进程向它写入与读取消息. 3.管道和命名管道都是通信数据都是先进先出的原则。 4.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。

目前主要有两种类型的消息队列:POSIX消息队列以及系统V消息队列,系统V消息队列目前被大量使用。系统V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。

二、相关函数

1. 获得key值

key_t ftok(char *pathname, int projid)

#include <sys/types.h> #include <sys/ipc.h> 参数: pathname:文件名(含路径),通常设置为当前目录“.” 比如projid为'a',则为"./a"文件 projid:项目ID,必须为非0整数(0-255).

2. 创建消息队列 int msgget(key_t key, int msgflag) #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> 功能: 用于创建一个新的或打开一个已经存在的消息队列,此消息队列与key相对应。 参数: key:函数ftok的返回值或IPC_PRIVATE。 msgflag: IPC_CREAT:创建新的消息队列。 IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。 IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞。 返回值: 调用成功返回队列标识符,否则返回-1.

在以下两种情况下,将创建一个新的消息队列: 1、如果没有与键值key相对应的消息队列,并且msgflag中包含了IPC_CREAT标志位。 2、key参数为IPC_PRIVATE。

3. 消息队列属性控制 int msgctl(int msqid, int cmd, struct msqid_ds *buf) 功能: 对消息队列进行各种控制操作,操作的动作由cmd控制。 参数: msqid:消息队列ID,消息队列标识符,该值为msgget创建消息队列的返回值。 cmd: IPC_STAT:将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构中. IPC_SET:将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值. IPC_RMID:删除由msqid指示的消息队列,将它从系统中删除并破坏相关数据结构. buf:消息队列缓冲区

代码语言:javascript
复制
 struct msqid_ds {
               struct ipc_perm msg_perm;          /* Ownership and permissions*/
               time_t         msg_stime;                 /* Time of last msgsnd() */
               time_t         msg_rtime;                  /* Time of last msgrcv() */
               time_t         msg_ctime;                 /* Time of last change */
               unsigned long  __msg_cbytes;    /* Current number of bytes in  queue (non-standard) */
               msgqnum_t      msg_qnum;          /* Current number of messages  in queue */
               msglen_t       msg_qbytes;           /* Maximum number of bytesallowed in queue */
               pid_t          msg_lspid;                  /* PID of last msgsnd() */
               pid_t          msg_lrpid;                  /* PID of last msgrcv() */
                     };
     struct ipc_perm {
               key_t key;                        /* Key supplied to msgget() */
               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;    /* Permissions */
               unsigned short seq;       /* Sequence number */
                   };

4.发送信息到消息队列 int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflag) #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> 功能:

将新消息添加到队列尾端,即向消息队列中发送一条消息。 参数: msqid:已打开的消息队列id msgp:存放消息的结构体指针。 msgflag:函数的控制属性。 消息结构msgbuf为: struct msgbuf { long mtype;//消息类型 char mtext[1];//消息正文,消息数据的首地址,这个数据的最大长度为8012吧,又可把他看成是一个结构,也有类型和数据,recv时解析即可。 } msgsz:消息数据的长度。 msgflag: IPC_NOWAIT: 指明在消息队列没有足够空间容纳要发送的消息时,msgsnd立即返回。 0:msgsnd调用阻塞直到条件满足为止.(一般选这个)

5. 从消息队列接收信息 ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtype, int msgflag) #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> 功能:

从队列中接收消息 参数: msqid:已打开的消息队列id msgp:存放消息的结构体指针。msgp->mtype与第四个参数是相同的。 msgsz:消息的字节数,指定mtext的大小。 msgtype:消息类型,消息类型 mtype的值。如果为0,则接受该队列中的第一条信息,如果小于0,则接受小于该值的绝对值的消息类型,如果大于0,接受指定类型的消息,即该值消息。 msgflag:函数的控制属性。 msgflag: MSG_NOERROR:若返回的消息比nbytes字节多,则消息就会截短到nbytes字节,且不通知消息发送进程. IPC_NOWAIT:调用进程会立即返回.若没有收到消息则返回-1. 0:msgrcv调用阻塞直到条件满足为止. 在成功地读取了一条消息以后,队列中的这条消息将被删除。

三、相关例子

例1:消息队列之简单收发测试

代码语言:javascript
复制
#include <sys/types.h>
#include <sys/msg.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
struct msg_buf{
     int mtype;//消息类型
     char data[255];//数据
};
int main(int argc,  char *argv[])
{
      key_t key;
      int msgid;
      int ret;
      struct msg_buf msgbuf;
      //获取key值
      key = ftok(".",  'a');
      printf("key = [%x]\n",  key);
      //创建消息队列
      msgid = msgget(key,  IPC_CREAT|0666);/*通过文件对应*/
      if(msgid == -1)
      {
            printf("creat error\n");
            return -1;
      }
      //以当前进程类型,非阻塞方式发送"test data"到消息队列
      msgbuf.mtype = getpid();
      strcpy(msgbuf.data,  "test data");
      ret = msgsnd(msgid,  &msgbuf,  sizeof(msgbuf.data),  IPC_NOWAIT);
      if(ret == -1)
      {
            printf("send message err\n");
            return -1;
      }
      //以非阻塞方式接收数据
      memset(&msgbuf,  0,  sizeof(msgbuf));
      ret = msgrcv(msgid,  &msgbuf,  sizeof(msgbuf.data), getpid(),  IPC_NOWAIT);
      if(ret == -1)
      {
            printf("receive message err\n");
            return -1;
      }
      printf("receive msg = [%s]\n",  msgbuf.data);
      return 0;
}

例2:进程间消息队列通信

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <string.h>
#include <signal.h>

struct msgbuf{
      int mtype;
      char mtext[100];
};
int main(void)
{
      key_t key;
      pid_t pid;
      int msgid;
      struct msgbuf msg;
      key=ftok(".", 0x01);
      if ( (msgid = msgget(key,  IPC_CREAT|0666)) <0 )
      {
            perror("msgget error");
            exit(1);
      }
      //创建一个进程
      if ( (pid = fork()) < 0 )
      {
            perror("fork error");
            exit(1);
      }
      //子进程收信息
      else if (pid==0)
      {
            while(1)
            {
                  memset(msg.mtext, 0, 100);
                  msgrcv(msgid, &msg, 100, 2, 0);  //receive the msg from 2
                  printf("\receive:%s\n:", msg.mtext);
                  fflush(stdout);
            }
            exit(0);
      }
      //父进程发信息
      else
      {
            while(1)
            {
                  memset(msg.mtext, 0, 100);
                  printf("father:");
                  fgets(msg.mtext, 100, stdin);
                  if (strncmp("bye", msg.mtext, 3)==0)//如果前3个字符为bye,则退出
                  {
                        kill(pid, SIGSTOP);
                        exit(1);
                  }
                  msg.mtype=1;//send to 1
                  msg.mtext[strlen(msg.mtext)-1]='\0';
                  msgsnd(msgid, &msg, strlen(msg.mtext)+1, 0);
            }
      }
      return 0;
}

这个程序的缺点为:有发无收,有收无发。可在这2个进程中分别创建2个线程,分别负责收和发,就完成了进程间的通信。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-02-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档