前言

  • 知识点

    • 消息队列、信号量 和 共享内存 被统称为 system-V IPC

      • 以上都是“持续性”资源,即它们被创建之后, 不会因为进程的退出而消失

4. 消息队列

4.1 概念

  • 消息队列

    • 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法

4.2 对比

  • 消息队列与信号之间的对比

    • 信号承载的信息量少,而消息队列可以承载大量自定义的数据
  • 消息队列与管道之间的对比
    • 相同

      • 进程间通信都可以是不相关的进程
      • 都可以独立于发送和接收进程而存在
      • 在进程终止时,其内容并不会被删除
    • 不同
      • 在命名管道中,发送数据用 write(),接收数据用 read(), 则在消息队列中,发送数据用 msgsnd(),接收数据用 msgrcv() ,消息队列对每个数据都有一个最大长度的限制
      • 管道只能承载无格式字节流,消息队列提供有格式的字节流
      • 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级,接收程序可以通过消息类型有选择地接收数据, 而不是像命名管道中那样,只能默认地接收
      • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的顺序接收,也可以按消息的类型接收

4.3 函数及使用流程

  • 使用流程:

    1. 使用 msgget() 来创建或打开消息队列
    2. 使用 msgsnd() 来发送消息到文末
    3. 使用 msgrcv() 来接收消息,可指定某一条消息
    4. 使用 msgctl() 来控制消息(具体往下看)

4.3.1 msgget()

  • 使用 msgget() 来创建或打开消息队列
  • 通过命令 man 了解更多
  • 函数原型:int msgget(key_t key, int msgflg);
    • key:消息队列的关键字值,多个进程可以通过它访问同一个消息队列
    • msgflg:表示创建的消息队列的模式标志参数,主要有IPC_CREAT,IPC_EXCL和权限mode,如:
      • IPC_CREAT:没有关键字 key 的消息队列就新建一个,有就直接打开
      • IPC_CREAT | IPC_EXCL:消息队列不存在,则新建一个,如果消息队列存在,则报错
      • IPC_CREAT | 0666:(注:消息队列不在意执行权限)
    • 返回
      • 成功:返回消息队列标识值
      • 失败:返回-1
        • 返回的错误码,即是在变量 error 中

          • EACCES:消息队列存在,但进程没有访问权限
          • EEXIST:msgflg 同时指定了 IPC_CREAT和IPC_EXCL,但是消息队列已经存在
          • ENOENT:消息队列不存在,且没有指定 IPC_CREAT 标志
          • ENOMEM:内存不足
          • ENOSPC:消息队列个数达到系统的限制

4.3.2 msgsng()

  • 使用 msgsnd() 来发送消息到文末
  • 通过命令 man 了解更多
  • 函数原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    • msqid:消息队列标识符
    • msgp:发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型, 即表明此发送消息的类型,msgrcv()函数则根据此接收消息
      /*msgp定义的参照格式*/
      struct s_msg{
      long type; /* 必须大于0,消息类型 */
      char mtext[1]; /* 消息正文,可以是其他任何类型 */
      } msgp;
    • msgsz:要发送消息的大小,不包含消息类型占用的4个字节
    • msgflg:
      • 0:当消息队列满时,msgsnd() 函数将会阻塞,直到消息能写进消息队列
      • IPC_NOWAIT:当消息队列已满的时候,msgsnd() 函数不等待立即返回
      • IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程
    • 返回
      • 成功:返回 0
      • 失败:返回 -1,错误原因存在于变量 error 中
        • EAGAIN:参数 msgflg 设为 IPC_NOWAIT,而消息队列已满
        • EIDRM:标识符为 msqid 的消息队列已被删除
        • EACCESS:无权限写入消息队列
        • EFAULT:参数 msgp 指向无效的内存地址
        • EINTR:队列已满而处于等待情况下被信号中断
        • EINVAL:无效的参数msqid、msgsz或参数消息类型type小于0
  • 解除阻塞的三个条件:
    1. 消息队列变为未满
    2. 消息队列被删除
    3. 调用 msgsnd() 的进程被信号中断

4.3.3 msgrcv()

  • 使用 msgrcv() 来发送消息到文末

  • 通过命令 man 了解更多

  • 函数原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

    • msqid:消息队列标识符
    • msgp:存放消息的结构体,结构体类型要与msgsnd()函数发送的类型相同
    • msgsz:要接收消息的大小,不包含消息类型占用的4个字节
    • msgtyp:
      • > 0:表示接收类型等于msgtyp的第一个消息
      • = 0:表示接收第一个消息
      • < 0:表示接收类型等于或者小于msgtyp绝对值的第一个消息
    • msgflg:
      • 0:阻塞式接收消息,没有该类型的消息 msgrcv 函数一直阻塞等待
      • IPC_NOWAIT:若在消息队列中并没有相应类型的消息可以接收,则函数立即返回,此时错误码为ENOMSG
      • IPC_EXCEPT:与 msgtype 配合使用返回队列中第一个类型不为 msgtype 的消息
      • IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的 size 字节,则把该消息截断,截断部分将被丢弃
    • 返回:
      • 成功:返回接收到的消息长度
      • 失败:返回 -1,错误原因存在于变量 error 中:
        • E2BIG:消息数据长度大于msgsz而msgflag没有设置IPC_NOERROR
        • EIDRM:消息队列已被删除
        • EACCESS:无权限读取该消息队列
        • EFAULT:参数msgp指向无效的内存地址
        • ENOMSG:参数msgflg设为IPC_NOWAIT,而消息队列中无消息可读
        • EINTR:等待读取队列内的消息情况下被信号中断
  • 解除阻塞的三个条件:

    1. 消息队列中已有符合条件的消息
    2. 消息队列被删除
    3. 调用 msgrcv() 的进程被信号中断

4.3.4 msgctl()

  • 使用 msgctl() 来设置或者获取消息队列的相关属性等等
  • 通过命令 man 了解更多
  • 函数原型:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    • msqid:消息队列标识符
    • cmd:操作命令
      • IPC_STAT:获取该 MSG 信息并存到结构体 msqid_ds 类型的 buf 中
      • IPC_SET:设置消息队列的属性,属性为 msqid_ds(msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes)
      • IPC_RMID:立即删除该 MSG,并且唤醒所有阻塞在该 MSG上的进程,同时忽略第三个参数
      • IPC_INFO:获得关于当前系统中 MSG 的限制值信息
      • MSG_INFO:获得关于当前系统中 MSG 的相关资源消耗信息
      • MSG_STAT:同 IPC_STAT,但 msgid 为该消息队列在内核中记录所有消息队列信息的数组的下标, 因此通过迭代所有的下标可以获得系统中所有消息队列的相关信息
    • buf:相关信息结构体缓冲区
    • 返回值
      • 成功:返回 0
      • 失败:返回 -1,错误原因存在 error 中:
        • EACCESS:参数cmd为IPC_STAT,却无权限读取该消息队列
        • EFAULT:参数buf指向无效的内存地址
        • EIDRM:标识符为msqid的消息队列已被删除
        • EINVAL:无效的参数cmd或msqid
        • EPERM:参数cmd为IPC_SET或IPC_RMID,却无执行权限

4.4 例程

  • 发送进程
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h> #define BUFFER_SIZE 512
/*定义消息结构体*/
struct message{
long msg_type;
char msg_text[BUFFER_SIZE];
};
int main()
{
int qid;
struct message msg; /*1. 创建消息队列*/
if ((qid = msgget((key_t)1234, IPC_CREAT|0666)) == -1)
{
perror("msgget\n");
exit(1);
} /*打印标识符*/
printf("Open queue %d\n",qid); while(1)
{
printf("Enter some message to the queue:");
if ((fgets(msg.msg_text, BUFFER_SIZE, stdin)) == NULL)
{
printf("\nGet message end.\n");
exit(1);
}
/*赋值消息类型*/
msg.msg_type = getpid();
/*2. 添加消息到消息队列*/
if ((msgsnd(qid, &msg, strlen(msg.msg_text), 0)) < 0)
{
perror("\nSend message error.\n");
exit(1);
}
else
{
printf("Send message.\n");
}
if (strncmp(msg.msg_text, "quit", 4) == 0)
{
printf("\nQuit get message.\n");
break;
}
}
exit(0);
}
  • 接收进程
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h> #define BUFFER_SIZE 512
/*定义消息结构体,与发送进程中的一样*/
struct message{
long msg_type;
char msg_text[BUFFER_SIZE];
};
int main()
{
int qid;
struct message msg; /*创建消息队列,键值和发送进程的一样*/
if ((qid = msgget((key_t)1234, IPC_CREAT|0666)) == -1)
{
perror("msgget");
exit(1);
}
/*打印标识符*/
printf("Open queue %d\n", qid);
do
{
/*读取消息队列*/
memset(msg.msg_text, 0, BUFFER_SIZE);
if (msgrcv(qid, (void*)&msg, BUFFER_SIZE, 0, 0) < 0)
{
perror("msgrcv");
exit(1);
}
printf("The message from process %ld : %s", msg.msg_type, msg.msg_text);
} while(strncmp(msg.msg_text, "quit", 4)); /*从系统内核中删除消息队列*/
if ((msgctl(qid, IPC_RMID, NULL)) < 0)
{
perror("msgctl");
exit(1);
}
else
{
printf("Delete msg qid: %d.\n", qid);
}
exit(0);
}

参考:

* 野火

【linux】系统编程-2-消息队列的更多相关文章

  1. Linux系统之《消息队列》入手应用

    目录 简述 代码 编译 运行 简述 消息队列是Linux进程间通信方式之一,消息队列一般是用于简单的通信,数据量不大,通信不频繁的情况.如果交互频繁或者数据量大就不适合了. 代码 下面直接上代码,发送 ...

  2. Linux系统编程@进程通信(一)

    进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...

  3. linux系统编程之框架

    linux系统编程之框架: 1. 进程 1.1 进程概念 1.1.1 PCB 1.1.2 环境变量 1.2 进程控制 1.3 进程间通信 1.3.1 管道 1.3.2 有名管道 1.3.3 共享内存 ...

  4. linux系统编程(一)概述

    glibc库封装了linux系统调用,并提供c语言接口 所以学习linux系统编程,主要参考glibc库系统调用相关api 一.进程控制: fork 创建一个新进程 clone 按指定条件创建子进程 ...

  5. Linux 系统编程 学习 总结

    背景 整理了Liunx 关于 进程间通信的 很常见的知识. 目录 与 说明 Linux 系统编程 学习:000-有关概念 介绍了有关的基础概念,为以后的学习打下基础. Linux 系统编程 学习:00 ...

  6. Linux 系统编程 学习:02-进程间通信1:Unix IPC(1)管道

    Linux 系统编程 学习:02-进程间通信1:Unix IPC(1)管道 背景 上一讲我们介绍了创建子进程的方式.我们都知道,创建子进程是为了与父进程协作(或者是为了执行新的程序,参考 Linux ...

  7. Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号

    Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号 背景 上一讲我们介绍了Unix IPC中的2种管道. 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道 ...

  8. Linux 系统编程 学习:04-进程间通信2:System V IPC(1)

    Linux 系统编程 学习:04-进程间通信2:System V IPC(1) 背景 上一讲 进程间通信:Unix IPC-信号中,我们介绍了Unix IPC中有关信号的概念,以及如何使用. IPC的 ...

  9. Linux 系统编程 学习:05-进程间通信2:System V IPC(2)

    Linux 系统编程 学习:05-进程间通信2:System V IPC(2) 背景 上一讲 进程间通信:System V IPC(1)中,我们介绍了System IPC中有关消息队列.共享内存的概念 ...

随机推荐

  1. SimpleChannelInboundHandler生命周期

    转载:https://www.pianshen.com/article/1766171597/

  2. Mybatis log plugin 破解!!!

    前言 今天重新装了IDEA2020,顺带重装了一些插件,毕竟这些插件都是习惯一直在用,其中一款就是Mybatis Log plugin,按照往常的思路,在IDEA插件市场搜索安装,艹,眼睛一瞟,竟然收 ...

  3. 树莓派自动连接WiFi

    使用sudo raspi-config配置好第一个wifi 然后只需要修改一个文件sudo nano /etc/wpa_supplicant/wpa_supplicant.conf 内容如下: ctr ...

  4. 什么是Python迭代器?

    迭代器(Iterator):迭代器可以看作是一个特殊的对象,每次调用该对象时会返回自身的下一个元素,从实现上来看,一个迭代器对象必须是定义了__iter__()方法和next()方法的对象. Pyth ...

  5. 转:解析HTTP协议六种请求方法,get,head,put,delete,post有什么区别

    解析HTTP协议六种请求方法,get,head,put,delete,post有什么区别 标准Http协议支持六种请求方法,即: 1.GET 2.POST 3.PUT 4.Delete 5.HEAD ...

  6. PyQt(Python+Qt)学习随笔:QAbstractItemView的verticalScrollMode和horizontalScrollMode属性

    老猿Python博文目录 老猿Python博客地址 一.概述 verticalScrollMode和horizontalScrollMode属性用于控制视图如何在垂直方向和水平方向滚动内容.滚动可以按 ...

  7. 安装ELK的一些问题

    1.使用的是centos6,其linux内核版本为2.6.而Elasticsearch的插件要求至少3.5以上版本 ,禁用这个插件即可.修改elasticsearch.yml文件,在最下面添加如下配置 ...

  8. A Simple Framework for Contrastive Learning of Visual Representations 阅读笔记

      Motivation 作者们构建了一种用于视觉表示的对比学习简单框架 SimCLR,它不仅优于此前的所有工作,也优于最新的对比自监督学习算法, 而且结构更加简单:这个结构既不需要专门的架构,也不需 ...

  9. 使用tkinter打造一个小说下载器,想看什么小说,就下什么

    前言 今天教大家用户Python GUI编程--tkinter 打造一个小说下载器,想看什么小说,就下载什么小说 先看下效果图 Tkinter 是使用 python 进行窗口视窗设计的模块.Tkint ...

  10. CSS3全览_文本+视觉+盒子+背景颜色

    CSS全览_文本+视觉+盒子+背景颜色 目录 CSS全览_文本+视觉+盒子+背景颜色 1. 文本属性 2. 视觉格式化基础 3. 内边距, 边框, 轮廓和外边距 4. 颜色, 背景和渐变 作者: ht ...