专栏首页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光明(pou0230),作者:PouG

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Pythpon 爬取中国天气网数据

    以前看别人用python写爬取数据的程序感觉特牛掰,今天在网上找到了一个例子参考了下,自己也写了一个。之后会结合微信机器人,然后每隔一段时间给自己和好友发送天气...

    用户5908113
  • Python爬取天气状况发送给微信好友

    上次和大家分享了爬取天气信息的python程序,程序上还有很多缺点,需要再了解一些html和css等相关知识后,才会把爬虫的程序做的更好。

    用户5908113
  • Qt事件系统与应用举例

    在Qt中,事件都是从抽象类QEvent派生出来的对象。它们表示发生在应用程序内部或由于应用程序需要了解的外部活动而发生的事情。 事件可以由QObject子类的任...

    用户5908113
  • 管程(Moniter): 并发编程的基本心法

    在吃透 Syncchronized 原理 中介绍了关于 Synchronize的实现原理,无论是同步方法还是同步代码块,无论是ACC_SYNCHRONIZED还...

    码哥字节
  • 如何避免相互依赖的系统间耦合

    两个应用熊中需要远程传递数据,常规的做法是直接进行远程调用,使用 Http,或者 其他 RMI 方式进行调用,但是这种方式将系统耦合起来,一旦被调用的系统产生了...

    王小明_HIT
  • 我是如何用 Webpack 虐待代码尺寸的 (第三回合)

    解释一下, 原因是 im 这个项目希望可以做到平台化, 具体来说就是, 这个项目拆成两个部分, 一部分是基础功能, 比如正常的聊天, 头像, 表情等, 另一部分...

    普通程序员
  • 动态方法和反射

    .net 2.0和.net 1.x相比在反射方面加强了很多,动态方法是.net 2.0新增的内容.具体参看MSDN 如何:定义和执行动态方法  卢彦 在co...

    张善友
  • 专家谈|中国AI凭什么就达不到世界一流水平?

    GAIR 今年夏天,雷锋网将在深圳举办一场盛况空前的“全球人工智能与机器人创新大会”(简称GAIR)。大会现场,谷歌,DeepMind,Uber,微软等巨头的人...

    AI科技评论
  • Python数据挖掘 环境搭建

    deepin 有很多小伙伴想学习python,但windows写python基本是坑,deepin属于linux分支,界面美观,内置大量桌面软...

    zhaoolee
  • python中的热更新或动态加载

    遍览网络中关于动态加载模块的文章,发现有两种方法,一种是用守护进程的方法,一种是用python自带的reload函数。

    py3study

扫码关注云+社区

领取腾讯云代金券