前言

  • 知识点

    • 消息队列、信号量共享内存 被统称为 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. 从零做网站开发:基于Flask和JQuery,实现表格管理平台

    摘要:本文将为大家带来基于Flask框架和JQuery实现管理平台网站的开发功能. [写在前面] 你要开发网站? 嗯.. 会Flask吗? 什么东西,没听过... 会JQuery吗? 是python的 ...

  2. oracle sql%notfound

    SQL%NOTFOUND 是一个布尔值.与最近的sql语句(update,insert,delete,select)发生交互,当最近的一条sql语句没有涉及任何行的时候,则返回true.否则返回fal ...

  3. k8s实验操作记录文档

    k8s实验操作记录文档,仅供学习参考! 文档以实验操作的过程及内容为主进行记录,涉及少量的介绍性文字(来自网络开源). 仅汇总主题所有链接,详细内容查看需要切换到相关链接.https://github ...

  4. day6(celery原理与组件)

    1.Celery介绍 1.1 celery应用举例 Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理,如果你的业务场景中需要用到异步任务,就可以考 ...

  5. Typescript + React 高仿 Antd 从零到一打造自己的组件库(完整)

    买了张轩老师的课程,感觉很不错,适用于高级进阶,老师讲的通俗易懂,欢迎讨论学习.WX:Jujiu_i

  6. ARL资产导出对接Xray扫描

    使用ARL资产灯塔系统对目标进行资产整理的时候,能够对获取的结果进行导出: 导出之后为excel文件 想要将site中的URL导出为txt文件,再使用Xray高级版进行批量化扫描: https://w ...

  7. Python自动发射弹幕

    Python自动发射弹幕,弹幕护体 - 环境: Python3+Windows- 开发工具: PyCharm 学习效果:1. 学会使用Python刷弹幕2. 配置INI文件信息3. 掌握网络请求知识4 ...

  8. git 常用命令 command

    git config --list //查看配置信息   git config user.name //查看用户名  git config user.email //查看用户邮箱 从远程克隆到本地仓库 ...

  9. MySQL入门看这一篇就够了

    MySQL JavaEE:企业级Java开发 web阶段 分为1.前端(页面,展示数据库中的数据) 2.后台(连接点:链接数据库JDBC.Mybatis,链接前端(控制视图跳转,给前端传递数据)) 3 ...

  10. 第三篇 Scrum 冲刺博客

    一.站立式会议 1. 会议照片 2. 工作汇报 团队成员名称 昨日(24日)完成的工作 今天(25日)计划完成的工作 工作中遇到的困难 陈锐基 - 个人信息编辑后与组件关联- 表白墙数据用 Vuex  ...