【Linux系统编程】进程间通信之消息队列
00. 目录
文章目录
- 00. 目录
- 01. 消息队列概述
- 02. 消息队列相关函数
- 03. 消息队列读写操作
- 04. 测试代码
- 05. 附录
01. 消息队列概述
消息队列提供了一种在两个不相关的进程之间传递数据的简单高效的方法,其特点如下:
1)消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
2)消息队列允许一个或多个进程向它写入或者读取消息。
3)与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。
4)每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
5)消息队列是消息的链表,存放在内存中,由内核维护。只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中。
02. 消息队列相关函数
消息队列常用操作函数如下:
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
int msgget(key_t key, int msgflg);
int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);
int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
对于消息队列的操作,我们可以类比为这么一个过程:假如 A 有个东西要给 B,因为某些原因 A 不能当面直接给 B,这时候他们需要借助第三方托管(如银行),A 找到某个具体地址的建设银行,然后把东西放到某个保险柜里(如 1 号保险柜),对于 B 而言,要想成功取出 A 的东西,必须保证去同一地址的同一间银行取东西,而且只有 1 号保险柜的东西才是 A 给自己的。
对于上面的例子,涉及到几个比较重要的信息:地址、银行、保险柜号码。
只有同一个地址才能保证是同一个银行,只有是同一个银行双方才能借助它来托管,只有同一个保险柜号码才能保证是对方托管给自己的东西。
而在消息队列操作中,键(key)值相当于地址,消息队列描述符相当于具体的某个银行,消息类型相当于保险柜号码。
同一个键(key)值可以保证是同一个消息队列,同一个消息队列描述符才能保证不同的进程可以相互通信,同一个消息类型才能保证某个进程取出是对方的信息。
键(key)值
System V 提供的进程间通信机制需要一个 key 值,通过 key 值就可在系统内获得一个唯一的消息队列标识符。key 值可以是人为指定的,也可以通过 ftok() 函数获得。
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
功能:获取键(key)值
参数:pathname: 路径名proj_id: 项目ID,非 0 整数(只有低 8 位有效)
返回值:成功:key 值失败:-1
创建消息队列函数
#include <sys/msg.h>int msgget(key_t key, int msgflg);
功能:创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数,只要用相同的 key 值就能得到同一个消息队列的标识符。参数:key: ftok() 返回的 key 值msgflg: 标识函数的行为及消息队列的权限,其取值如下:IPC_CREAT:创建消息队列。IPC_EXCL: 检测消息队列是否存在。位或权限位:消息队列位或权限位后可以设置消息队列的访问权限,格式和open() 函数的 mode_t一样(open() 的使用请点此链接),但可执行权限未使用。返回值:成功:消息队列的标识符失败:-1
参考代码如下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>int main(int argc, char *argv[])
{key_t key;int msgqid;key = ftok(".", 2012); // key 值// 创建消息队列msgqid = msgget(key, IPC_CREAT|0666);return 0;
}
测试结果:
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out #查看消息队列
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ipcs -q--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0xdc320009 0 deng 666 0 0 #删除消息队列
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ipcrm -q 0
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ipcs -q--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息 deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$
03. 消息队列读写操作
对于消息队列的读写,都是以消息类型为准。消息类型相当于保险柜号码,A 往 1 号保险柜放东西,对方想取出 A 的东西必须也是从 1 号保险柜里取。同理,某一进程往消息队列添加 a 类型的消息,别的进程要想取出这进程添加的信息也必须取 a 类型的消息。
在学习消息队列读写操作前,我们先学习消息队列的消息格式:
typedef struct _msg
{long mtype; // 消息类型char mtext[100]; // 消息正文//…… …… // 消息的正文可以有多个成员
}MSG;
消息类型必须是长整型的,而且必须是结构体类型的第一个成员,类型下面是消息正文,正文可以有多个成员(正文成员可以是任意数据类型的)。至于这个结构体类型叫什么名字,里面成员叫什么名字,自行定义,没有明文规定。
发送消息
#include <sys/msg.h>int msgsnd( int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:将新消息添加到消息队列。
参数:msqid: 消息队列的标识符。msgp: 待发送消息结构体的地址。msgsz: 消息正文的字节数。msgflg:函数的控制属性,其取值如下:0:msgsnd() 调用阻塞直到条件满足为止。IPC_NOWAIT: 若消息没有立即发送则调用该函数的进程会立即返回。
返回值:成功:0失败:-1
接收消息
#include <sys/msg.h>ssize_t msgrcv( int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg );
功能:从标识符为 msqid 的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除。
参数:msqid:消息队列的标识符,代表要从哪个消息列中获取消息。msgp: 存放消息结构体的地址。msgsz:消息正文的字节数。msgtyp:消息的类型。可以有以下几种类型:msgtyp = 0:返回队列中的第一个消息。msgtyp > 0:返回队列中消息类型为 msgtyp 的消息(常用)。msgtyp < 0:返回队列中消息类型值小于或等于 msgtyp 绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。注意:在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原则。msgflg:函数的控制属性。其取值如下:0:msgrcv() 调用阻塞直到接收消息成功为止。MSG_NOERROR: 若返回的消息字节数比 nbytes 字节数多,则消息就会截短到 nbytes 字节,且不通知消息发送进程。IPC_NOWAIT: 调用进程会立即返回。若没有收到消息则立即返回 -1。
返回值:成功:读取消息的长度失败:-1
消息队列控制
#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列。
参数:msqid:消息队列的标识符。cmd:函数功能的控制。其取值如下:IPC_RMID:删除由 msqid 指示的消息队列,将它从系统中删除并破坏相关数据结构。IPC_STAT:将 msqid 相关的数据结构中各个元素的当前值存入到由 buf 指向的结构中。相对于,把消息队列的属性备份到 buf 里。IPC_SET:将 msqid 相关的数据结构中的元素设置为由 buf 指向的结构中的对应值。相当于,消息队列原来的属性值清空,再由 buf 来替换。buf:msqid_ds 数据类型的地址,用来存放或更改消息队列的属性。返回值:成功:0失败:-1
04. 测试代码
写消息队列代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>typedef struct _msg
{long mtype;char mtext[50];
}MSG;int main(int argc, char *argv[])
{key_t key;int msgqid;MSG msg;key = ftok("./", 2015); // key 值// 创建消息队列msgqid = msgget(key, IPC_CREAT|0666);if(msgqid == -1){perror("msgget");exit(-1);}msg.mtype = 10; // 消息类型strcpy(msg.mtext, "hello mike"); // 正文内容/* 添加消息msg_id:消息队列标识符&msg:消息结构体地址sizeof(msg)-sizeof(long):消息正文大小0:习惯用0*/msgsnd(msgqid, &msg, sizeof(msg)-sizeof(long), 0);return 0;
}
读消息队列代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>typedef struct _msg
{long mtype;char mtext[50];
}MSG;int main(int argc, char *argv[])
{key_t key;int msgqid;key = ftok("./", 2015); // key 值// 创建消息队列msgqid = msgget(key, IPC_CREAT|0666);if(msgqid == -1){perror("msgget");exit(-1);}MSG msg;memset(&msg, 0, sizeof(msg));/* 取出类型为 10 的消息msg_id:消息队列标识符&msg:消息结构体地址sizeof(msg)-sizeof(long):消息正文大小(long)10:消息的类型0:习惯用0*/msgrcv(msgqid, &msg, sizeof(msg)-sizeof(long), (long)10, 0);printf("msg.mtext=%s\n", msg.mtext); // 把消息队列删除// IPC_RMID:删除标志位msgctl(msgqid, IPC_RMID, NULL);return 0;
}
写端代码测试:
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ipcs -q--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0xdf320009 32768 deng 666 56 1 deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$
读端代码测试:
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out
msg.mtext=hello mike
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ipcs -q--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息 deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$
05. 附录
【Linux系统编程】进程间通信之消息队列相关推荐
- Linux系统编程—进程间通信—信号量
信号量 信号量是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用.在进入一个关键代码段之前,线程必须获取一个信号量:一旦该关键代码段完成了,那么该线程必须释放信号量.其它想 ...
- Linux系统编程—进程间通信—共享内存
Linux共享内存 共享内存就是允许两个或多个进程共享一定的存储区.就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针.当一个进程改变了这块地址中的内容的时候,其它进程都会察觉 ...
- Linux系统编程——进程间通信:命名管道(FIFO)
命名管道的概述 无名管道,由于没有名字,只能用于亲缘关系的进程间通信(更多详情,请看<无名管道>).为了克服这个缺点,提出了命名管道(FIFO),也叫有名管道.FIFO 文件. 命名管道( ...
- 入门Linux系统编程--网络编程
文章目录 一.网络编程 1.socket服务端代码实现(无连接客户端) 6.socket服务端代码实现(连接客户端) 7.socket客户端代码实现 8.实现双方聊天 9.多方消息收发 二.往期文章 ...
- c语言系统编程八:Linux进程间通信之消息队列
Linux进程间通信之消息队列 一 消息队列概述 二 消息队列的特点 三 消息队列的创建和使用 3.1 获取系统唯一的key值 3.2 创建消息队列 3.3 查看消息队列和删除消息队列的shell命令 ...
- Linux系统编程(七)消息队列
Linux系统编程(七)消息队列 一.什么是消息队列 二.消息队列内部原理 三.实现消息队列的收发 1.发送消息队列 2.接收消息队列 四.消息队列与命名管道的比较 一.什么是消息队列 消息队列提供了 ...
- Linux C编程--进程间通信(IPC)5--System V IPC 机制1--消息队列
System V IPC 机制 1.基本概念 IPC对象一经创建,系统内核即为该对象分配相关的数据结构.为方便对IPC对象的管理,Linux提供了专门的IPC控制命令,主要包括查看IPC对象信息的ip ...
- 实验六 Linux进程编程,Linux系统编程实验六:进程间通信
<Linux系统编程实验六:进程间通信>由会员分享,可在线阅读,更多相关<Linux系统编程实验六:进程间通信(10页珍藏版)>请在人人文库网上搜索. 1.实验六:进程间通信l ...
- 【Linux | 系统编程】Linux系统编程(文件、进程线程、进程间通信)
文章目录 Linux系统编程 文件IO open/close函数 read/write函数 文件描述符 阻塞.非阻塞 fcntl函数 lseek函数 传入传出参数 文件系统 文件存储 文件操作 sta ...
最新文章
- Android数据持久化:SharePreference
- 微软在Build 2019大会上发布Fluid Framework协作平台
- CFtpFileFind FindFile卡住的问题
- linux电视改安卓,mstar安卓智能电视方案源代码常用修改
- CCNA考试试验总结
- linux命令音乐视频合并,Linux下基于命令行的音乐播放器 (1)
- 前端工作、学习中常用工具推荐
- 从gb2py.idx中获取一个汉字的拼音首字母
- linux opendir php,php目录遍历函数opendir用法实例
- 2008最火爆的十大网络流行语
- 《图说设计模式》读书笔记
- 基于SSM实现的新闻发布系统【附源码】(毕设)
- 京东供应商协同平台 客户评价数据导出python
- 读《深入理解计算机系统》
- 独孤思维:减压还能赚钱?让压力变有利可图的项目
- k-means算法及python实现
- 饼图 + 不规则区域事件处理
- CodeBlocks搭建OpenCV
- Web 年会抽奖系统(转盘抽奖、图片抽奖)可设置
- gif文件在linux下怎么打开,gif文件扩展名,gif文件怎么打开?
热门文章
- ExtJS学习:MVC模式案例(三)
- M2 Planning Day3
- 两分钟彻底让你明白Android Activity生命周期(图文)!
- Python测试演讲稿收集
- html逻辑判断符,JavaScript中的逻辑判断符、||与!介绍
- 需求分析 应该先写业务还是功能_产品经理必知:产品调研中功能调研的标准“姿势”...
- 实验7.3 字符串 7-5 查找指定字符
- java 自定义文件后缀名_Java 的源代码文件的扩展名是( )。_学小易找答案
- arcgis dem栅格立体感_如何使用ArcGIS从DEM数据中提取水系
- Java黑皮书课后题第5章:5.8(找出得最高分的学生)编写程序,提示用户输入学生的个数、每个学生名字及分数,最后显示获得最高分的学生