【linux】系统编程-2-消息队列
前言
- 知识点
- 消息队列、信号量 和 共享内存 被统称为 system-V IPC
- 以上都是“持续性”资源,即它们被创建之后, 不会因为进程的退出而消失
- 消息队列、信号量 和 共享内存 被统称为 system-V IPC
4. 消息队列
4.1 概念
- 消息队列
- 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法
4.2 对比
- 消息队列与信号之间的对比
- 信号承载的信息量少,而消息队列可以承载大量自定义的数据
- 消息队列与管道之间的对比
- 相同
- 进程间通信都可以是不相关的进程
- 都可以独立于发送和接收进程而存在
- 在进程终止时,其内容并不会被删除
- 不同
- 在命名管道中,发送数据用 write(),接收数据用 read(), 则在消息队列中,发送数据用 msgsnd(),接收数据用 msgrcv() ,消息队列对每个数据都有一个最大长度的限制
- 管道只能承载无格式字节流,消息队列提供有格式的字节流
- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级,接收程序可以通过消息类型有选择地接收数据, 而不是像命名管道中那样,只能默认地接收
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的顺序接收,也可以按消息的类型接收
- 相同
4.3 函数及使用流程
- 使用流程:
- 使用 msgget() 来创建或打开消息队列
- 使用 msgsnd() 来发送消息到文末
- 使用 msgrcv() 来接收消息,可指定某一条消息
- 使用 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:消息队列个数达到系统的限制
- 返回的错误码,即是在变量 error 中
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
- 解除阻塞的三个条件:
- 消息队列变为未满
- 消息队列被删除
- 调用 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:等待读取队列内的消息情况下被信号中断
解除阻塞的三个条件:
- 消息队列中已有符合条件的消息
- 消息队列被删除
- 调用 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-消息队列的更多相关文章
- Linux系统之《消息队列》入手应用
目录 简述 代码 编译 运行 简述 消息队列是Linux进程间通信方式之一,消息队列一般是用于简单的通信,数据量不大,通信不频繁的情况.如果交互频繁或者数据量大就不适合了. 代码 下面直接上代码,发送 ...
- Linux系统编程@进程通信(一)
进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...
- 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 共享内存 ...
- linux系统编程(一)概述
glibc库封装了linux系统调用,并提供c语言接口 所以学习linux系统编程,主要参考glibc库系统调用相关api 一.进程控制: fork 创建一个新进程 clone 按指定条件创建子进程 ...
- Linux 系统编程 学习 总结
背景 整理了Liunx 关于 进程间通信的 很常见的知识. 目录 与 说明 Linux 系统编程 学习:000-有关概念 介绍了有关的基础概念,为以后的学习打下基础. Linux 系统编程 学习:00 ...
- Linux 系统编程 学习:02-进程间通信1:Unix IPC(1)管道
Linux 系统编程 学习:02-进程间通信1:Unix IPC(1)管道 背景 上一讲我们介绍了创建子进程的方式.我们都知道,创建子进程是为了与父进程协作(或者是为了执行新的程序,参考 Linux ...
- Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号
Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号 背景 上一讲我们介绍了Unix IPC中的2种管道. 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道 ...
- Linux 系统编程 学习:04-进程间通信2:System V IPC(1)
Linux 系统编程 学习:04-进程间通信2:System V IPC(1) 背景 上一讲 进程间通信:Unix IPC-信号中,我们介绍了Unix IPC中有关信号的概念,以及如何使用. IPC的 ...
- Linux 系统编程 学习:05-进程间通信2:System V IPC(2)
Linux 系统编程 学习:05-进程间通信2:System V IPC(2) 背景 上一讲 进程间通信:System V IPC(1)中,我们介绍了System IPC中有关消息队列.共享内存的概念 ...
随机推荐
- 区块链V1版本实现之四
部分程序代码(添加区块): //添加区块 func (bc *BlockChain) AddBlock(data string) { //创建一个区块 //bc.Block的最后一个区块的Hash值就 ...
- SkyWalking —— 分布式应用监控与链路追踪
SkyWalking 是一个应用性能监控系统,特别为微服务.云原生和基于容器(Docker, Kubernetes, Mesos)体系结构而设计.除了应用指标监控以外,它还能对分布式调用链路进行追踪. ...
- 「刷题笔记」DP优化-状压-EX
棋盘 需要注意的几点: 题面编号都是从0开始的,所以第1行实际指的是中间那行 对\(2^{32}\)取模,其实就是\(unsigned\ int\),直接自然溢出啥事没有 棋子攻击范围不会旋转 首先, ...
- mininet + opendaylight环境配置
环境配置 ubuntu18.04 镜像 mininet2.2.2 apt-get install mininet 但这种安装只是TLS版本的mininet,与最新版本在功能上有所差距. 控制器(ope ...
- 真香,理解记忆法学习Python基础语法
这篇文章很难写!我最开始学 Python,和大多数人一样,是看的菜鸟教程: 在写完这篇文章的第一遍后,我发现并没有写出新意,很可能读者看到后,会和我当初一样,很快就忘了.我现在已经不是读者而是作者了, ...
- 基于java实现的简单网页日历功能,有兴趣得可以把它转换到前端实现
之前做项目的时候,因为要用到不同日期显示不同的内容,就自己做了一个日期的显示和选择功能,今天抽空把以前的代码理了一下,顺便就把之前做的日期功能给拿出来回顾一下,大家可以提点意见,帮忙完善下设计.先上一 ...
- 从零开始的ssti学习(已填)
前前言: 本文只是接这个机会来梳理一下ssti的知识点.先说一下,本文目前的重点是Flask的ssti,但是之后会填其他框架的坑.(就不该叫ssti学习,ssti太广了) 涉及知识点: 模板注入 前言 ...
- Leetcode学习笔记(2)
题目1 ID面试题 01.04 给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一. 回文串是指正反两个方向都一样的单词或短语.排列是指字母的重新排列. 回文串不一定是字典当中的单词. 示例 ...
- FOFA链接爬虫爬取fofa spider
之前一直是用的github上别人爬取fofa的脚本,前两天用的时候只能爬取第一页的链接了,猜测是fofa修改了一部分规则(或者是我不小心删除了一部分文件导致不能正常运行了) 于是重新写了一下爬取fof ...
- CommandLineRunner 可能会导致你的应用宕机停止,我劝你耗子尾汁
hello,大家好,我是小黑,又和大家见面啦~~ 如果你去某度搜索关键词 CommandLineRunner 初始化资源 ,截止小黑同学写这篇推文之前,大概能收到 1,030,000 个结果. 网上大 ...