菜鸟程序员利用消息队列实现进程间通信

本文深入探讨了进程间通信中的消息队列概念,通过代码示例详细解释了如何在Linux环境中实现消息队列的创建、消息添加及读取,展示了其在随机消息查询方面的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前介绍了进程间通信的PIPE通信、FIFO通信和共享内存,三种通信方式各有其适用范围。

今天介绍第四种进程通信方式—消息队列。

消息队列的概念

消息队列从字面理解就是消息组成的列表。进程能够从消息队列添加消息和读取消息。

乍一看消息队列类似于FIFO通信,但消息队列能够实现消息的随机查询,有些读者会疑惑这是什么意思呢?

FIFO中的信息必须按照信息的先后顺序进行读取,而消息队列能够指定读取某条消息,即不必按照顺序读取消息。

另外,进程通过消息队列添加和读取的消息也保存在linux内核中,由“队列ID”进行标识。

消息队列的实现步骤

消息队列的实现较为简单,分为以下步骤:

(1)创建并打开消息队列。通过函数msgget()创建并打开消息队列。

(2)添加消息。通过函数msgsnd()函数将进程的消息添加到消息队列中。

(3)读取消息。通过函数msfrcv()函数把消息从消息队列读取到进程中。

NOTE:在创建消息队列时,需要利用ftok函数将一条已存在的路径和一个整数转换成类型为key_t的键值,这是由于msgget()函数需要利用ftok的返回值生成消息队列的ID。

消息队列的代码实现

下面是共享内存的代码实现(在linux环境下编译通过):

发送进程的代码实现:

//发送进程
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX_BUFFER_LEN 1024

//定义消息结构体
struct message
{
  long msg_type;
  char msg_text[MAX_BUFFER_LEN];
};

int main()
{
  int msg_id;
  key_t key;
  struct message msg;
  
  key = ftok(".", 'a');//利用ftok函数将当前目录与'a'共同转化为key_t类型的键值
  if(key == -1)
  {
    perror("ftok");
    exit(1);
  }

  msg_id = msgget(key, IPC_CREAT|0666);//创建消息队列,并设置文件权限掩码0666
  if(msg_id == -1)
  {
    perror("creat msg");
    exit(1);
  }
  else
  {
    printf("the message queue ID is %d\n", msg_id);
  }
  
  while(1)
  {
    printf("enter some message to the queus:");
    if(fgets(msg.msg_text, MAX_BUFFER_LEN, stdin) == NULL)
    {
      puts("no message");
      exit(1);
    }
    msg.msg_type = getpid();
    if(msgsnd(msg_id, &msg, strlen(msg.msg_text), 0) == -1)//添加消息到消息队列
    {
      perror("message posted.\n");
      exit(1);
    }
    if(strncmp(msg.msg_text, "quit", 4) == 0)//如果接收的消息为quit,则退出接收进程
    {
      break;
    }
  }

  exit(0);//发送进程正常结束
  return 0;
}

接收进程的代码实现:

//接收进程
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

#define MAX_BUFFER_LEN 1024 

//定义消息的结构体
struct message
{
  long msg_type;
  char msg_text[MAX_BUFFER_LEN];

};

int main()
{
  int msg_id;
  int key;
  struct message msg;
  
  key = ftok(".", 'a');//利用ftok函数将当前目录与'a'共同转化为key_t类型的键值
  if(key == -1)
  {
    perror("creat key");
    exit(1);
  }

  msg_id = msgget(key, IPC_CREAT|0666);//创建消息队列,并设置文件权限掩码0666
  if(msg_id == -1)
  {
    perror("get the message queue");
    exit(1);
  }
  else
  {
    printf("receive message ID is %d\n", msg_id);
  }

  while(1)
  {
    memset(msg.msg_text, 0, MAX_BUFFER_LEN);//将消息队列清空
    if(msgrcv(msg_id, &msg, MAX_BUFFER_LEN, 0, 0) == -1)//接收进程从消息队列读取消息
    {
      perror("receive mess, error");
      exit(1);
    }
    else
    {
      printf("the message is %s\n", msg.msg_text);
    }

    if(strncmp(msg.msg_text, "quit", 4) == 0)//如果接收的消息为quit,则退出接收进程
    {
      break;
    }
  }
  
  if(msgctl(msg_id, IPC_RMID, NULL) < 0)//IPC_RMID为从进程中删除消息队列
  {
    perror("msgctl");
    exit(1);
  }
  exit(0);//接收进程正常结束
  return 0;
}

Note:接收进程和发送进程均利用msgget函数创建消息队列,由于使用的消息队列的键值一致,所以返回的消息队列ID也是一样的,从而实现进程间消息传递。

下图是运行结果:
在这里插入图片描述

Note:在两个终端中分别运行接收进程和发送进程。

总结

消息队列是进程间通信的一种常用方式,其广泛应用于实际项目中多进程的通信,感兴趣的读者可以自己在电脑上尝试实现消息队列通信。

当然,进程间的通信方式还有socket、信号和信号量。关于socke的通信原理还在学习中,后续将在公众号上更新。关于信号和信号量将会在线程的同步与互斥中介绍。

ps: 欢迎关注我的公众号[酷酷的coder],分享转行菜鸟程序员成长过程汇总的烦恼和反思.
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值