专栏首页Pou光明Linux进程间通信——消息队列(一)

Linux进程间通信——消息队列(一)

我学习一个东西,喜欢先从整体上了解框架,然后再了解所学习的东西是框架中的哪一细分部分。今天就聊一聊Linux系统进程之间的通信。

程序环境:ubuntu16.04 x_64 虚拟机

一、站得高,望得远

有三种IPC(进程间通讯)我们称作XSI IPC,即消息队列、信号量和共享内存

1. XSI IPC

①POSIX标准 Portable Operating System Interface(可移植操作系统接口)

②Single UNIX Specification是POSIX的超集

③X/Open System Interface(XSI IPC)

符合Single UNIX规范的系统的核心应用程序编程接口

有点儿蒙圈吧,正常正常~

个人理解:听说过POSIX多线程程序设计吧,就是符合①的可移植操作系统接口的多线程设计,然后②又是①的超集,再然后③是符合②的......可能很多人就是因为这些才不想学一些东西吧,不过这些不清楚也没多大关系

2. 进程间通信分类

进程间数据通信必须通过内核,因为不同进程的用户地址空间是不同的,他们各自的全局变量是不可见的。所以他们通过在内核地址上开辟出一段空间来进行数据传输。

进程间通信根据是否在同一台主机上进行通信可分为无名管道和有名管道(FIFO),消息队列、信号量和共享内存这些都是只能在同一台主机上进行通信的

Socket和Streams(这个没接触过)是可以在不同主机上进行进程通讯的。

3. 进程间通信之管道简介

①无名管道

②有名管道

无名管道的限制:半双工

两个进程需要有公共祖先

有名管道举例:当在终端连续使用两个命令时,一条命令的输出通过管道作为另一条命令的输入。

二、XSI IPC的使用与注意事项

1. 标识符和Key

每个内核中的IPC结构(消息队列、信号量、共享内存)都用一个非负整数的标识符来进行调用。如,当使用消息队列发送或接收消息队列时,需要知道队列标识符。

标识符是IPC内部的名称,在外部通信时使用Key作为标识符,每个IPC对象都与一个Key相关联。

2. 新建Key的方法及注意事项

get函数的两个参数分别是Key和一个整型flag(之后会介绍get函数)

①Key是IPC_PRIVATE

②Key当前未与特定的IPC结构相结合,并且flag中指定了IPC_CREAT位

③ftok(暂不具体介绍)

当访问已存在的队列时,Key值必须与创建队列时指定的Key值相同,且不应指定IPC_CREAT

注意:①为了访问一个现存的队列,决不能指定IPC_PRIVATE作为Key,因为它总是用于创建一个新队列。

②如果希望新建一个消息队列,而且要确保不是引用具有同一标识符的现有的消息队列,需在flag中指定IPC_CREAT和IPC_EXCL。这样,如果消息队列已经存在则返回值会报错。

3. 三种形式XSI IPC结构限制

我的系统默认限制如下:

4. 优点和缺点

XSI IPC的主要问题是:IPC结构是在系统范围内起作用的,没有引用计数。这点可以类比C++的智能指针。例如:如果进程创建 了一个消息队列,并在队列中放入了几条消息,然后进程终止,但是该消息队列及其内容并不会被删除。

当以下情况出现时消息队列才不会继续存在系统中:

①某个进程调用msgrcv或msgctl读取或删除消息队列

②某个进程执行ipcrm(1)命令删除息队列

与管道相比,最后一个访问管道的进程结束时,管道就彻底被删除了(可与智能指针类比)。

5. 程序通信例子

①Send:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>

#define MAX_TEXT 512
struct msg_st
{
  long int msg_type;
  char text[MAX_TEXT];
};

int main()
{
  int running = 1;
  struct msg_st data;
  char buffer[BUFSIZ];
  int msgid = -1;

  //建立消息队列
  msgid = msgget((key_t)1234, 0666 | IPC_CREAT | IPC_EXCL);
  if(msgid == -1)
  {
    fprintf(stderr, "msgget failed with error: %d\n", errno);
    exit(EXIT_FAILURE);
  }

  //向消息队列中写消息,直到写入end
  while(running)
  {
    //输入数据
    printf("Enter some text: ");
    fgets(buffer, BUFSIZ, stdin);
    data.msg_type = 1;    //注意2
    strcpy(data.text, buffer);
    //向队列发送数据
    if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
    {
      fprintf(stderr, "msgsnd failed\n");
      exit(EXIT_FAILURE);
    }

    printf("You wrote: %s\n",data.text);

    //输入end结束输入
    if(strncmp(buffer, "end", 3) == 0)
      running = 0;
    sleep(1);
  }
  exit(EXIT_SUCCESS);
}

②Rcv:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>

struct msg_st
{
  long int msg_type;
  char text[BUFSIZ];
};

int main()
{
  int running = 1;
  int msgid = -1;
  struct msg_st data;
  long int msgtype = 0; //注意1

  //建立消息队列
  //msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
  msgid = msgget((key_t)1234, 0666);
  if(msgid == -1)
  {
    fprintf(stderr, "msgget failed with error: %d\n", errno);
    exit(EXIT_FAILURE);
  }
  //从队列中获取消息,直到遇到end消息为止
  while(running)
  {
    if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)
    {
      fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
      exit(EXIT_FAILURE);
    }
    printf("You wrote: %s\n",data.text);
    //遇到end结束
    if(strncmp(data.text, "end", 3) == 0)
      running = 0;
  }
  //删除消息队列
  if(msgctl(msgid, IPC_RMID, 0) == -1)
  {
    fprintf(stderr, "msgctl(IPC_RMID) failed\n");
    exit(EXIT_FAILURE);
  }
  exit(EXIT_SUCCESS);
}

③程序运行效果

发送效果:

接收效果:

运行发送程序,根据提示输入字符串,接收端会收到字符,输入end消息队列

终止。

三、小结

程序就是网上最流行的例子,做了微小的改动,下面想几个问题:

①发送和接收可以对同一个Key多次使用不同的进程访问么?如果可以效果是什么样的,一对多还是多对一?多次访问属于正常操作么?

②使用什么方式让发送端与接收端都知道Key值呢?

③下次具体介绍api时还有其他精彩的用法

参考书籍 《UNIX环境高级编程第三版》

阅读一手资料,多思考,还是挺好的。

文章分享自微信公众号:
Pou光明

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

如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • Linux进程间通信 消息队列

    简单理解,消息队列就是一堆消息的有序集合,并缓存于内核中。如此一来,多个进程就可通过访问内核来实现多个进程之间的通信。目前存在的消息队列有POSIX与Syste...

    开源519
  • Linux进程间通信之消息队列

      马上过年了,心里万般滋味。。。 一,消息队列 1,概念:“消息队列”是在消息的传输过程中保存消息的容器 2,消息队列就是一个消息的链表。可以把消息看作一个记...

    xcywt
  • Linux 的进程间通信:消息队列

    Linux 环境提供了 XSI 和 POSIX 两套消息队列,本文将帮助您掌握以下内容:如何使用 XSI 消息队列,如何使用 POSIX 消息队列,它们的底层实...

    邹立巍
  • Linux进程间通信(二) - 消息队列

    消息队列 消息队列是Linux IPC中很常用的一种通信方式,它通常用来在不同进程间发送特定格式的消息数据。 消息队列和之前讨论过的管道和FIFO有很大的区别,...

    三丰SanFeng
  • Linux系统编程——进程间通信:消息队列

    对于消息队列的操作,我们可以类比为这么一个过程:假如 A 有个东西要给 B,因为某些原因 A 不能当面直接给 B,这时候他们需要借助第三方托管(如银行),A 找...

    马修
  • Linux系统编程-进程间通信(消息队列)

    前面文章介绍了Linux下进程的创建,管理,陆续介绍了进程间通信的方式:管道、内存映射、共享内存等。这篇文章继续介绍Linux的进程间通信方式消息队列。

    DS小龙哥
  • Linux进程通信之消息队列

    在PHP扩展中,封装了8个消息队列相关的函数,有 ftok、msg_get_queue、msg_queue_exists、msg_receive、msg_rem...

    北溟有鱼QAQ
  • swoole进程间通信之消息队列

    这样,即使进程重启,依然可以延用之前的队列,队列中若有未处理完的数据可以继续通过pop()取得。

    跑马溜溜的球
  • UNIX(进程间通信):10 消息队列

    1.消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识. 2.消息队列允许一个或多个进程向它写入与读取消息. 3....

    用户3479834
  • 进程通信(二)消息队列(System V 消息队列)

    消息队列:消息队列的本质是由Linux内核创建用于存放消息的链表,并且其功能是用来存放消息的,所以又称之为消息队列。 在Linux的不同进程中,包括有血缘的进...

    lexingsen
  • Linux进程间通信(上)之管道、消息队列实践

    进程间通信的几种方式:无名管道、有名管道、消息队列、共享内存、信号、信号量、套接字(socket)。

    杨源鑫
  • php进程通信-消息队列

    php多进程通信,有各种各样的方法(进程信号,消息队列,管道,共享内存,socket等等)

    仙士可
  • Linux 下的进程间通信:使用管道和消息队列

    本篇是 Linux 下进程间通信(IPC)系列的第二篇文章。第一篇文章 聚焦于通过共享文件和共享内存段这样的共享存储来进行 IPC。这篇文件的重点将转向管道,它...

    用户8639654
  • [PHP] 多进程通信-消息队列使用

    陶士涵
  • linux 进程通信-消息队列(Message queue)《Rice linux 学习开发》

    Rice加饭
  • PHP多进程通信-消息队列使用

    以上所述是小编给大家介绍的PHP通信-消息队列使用详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对ZaLo...

    砸漏

扫码关注云+社区

领取腾讯云代金券