Linux间进程通信--消息队列
本系列文章主要是学习记录Linux下进程间通信的方式。
常用的进程间通信方式:管道、FIFO、消息队列、信号量以及共享存储。
参考文档:《UNIX环境高级编程(第三版)》
参考视频:Linux进程通信 推荐看看,老师讲得很不错
Linux核心版本:2.6.32-431.el6.x86_64
注:本文档只是简单介绍IPC,更详细的内容请查看参考文档和相应视频。
本文介绍利用消息队列进行进程间的通信。
1 IPC对象
IPC对象:消息队列、共享内存和信号量。
存在于内核中而不是文件系统中,由用户控制释放(用户管理ipc对象的生命周期),不像管道那样由内核控制。
IPC对象通过标识符来引用和访问,所有IPC在内核空间中有唯一标识ID,在用户空间中的唯一标识称为key。
可通过[root@192 ~]# ipcs 命令查看。
每个IPC对象都由get函数创建:msgget、shmget、semget,调用get函数必须指定关键字key。
2 介绍
- 消息队列是内核中的一个链表。
- 消息队列存储在内核中,由消息队列标识符标识。
- 用户进程将数据(二进制、文本等)传输到内核后,内核重新添加一些如用户ID、组ID、读写进程的ID和优先级等相关信息后并打成一个数据包称为消息。
- 允许一个或多个进程往消息队列中写消息和读消息,但一个消息只能被一个进程读取,读取完毕后就自动删除。
- 消息队列具有一定的FIFO的特性,消息可以按照顺序发送到队列中,也可以几种不同的方式从队列中读取。每一个消息队列在内核中用一个唯一的IPC标识ID表示。
- 消息队列的实现包括创建和打开队列、读取消息和控制消息队列等四种操作。
3 消息队列属性结构体
1 struct msqid_ds {
2 struct ipc_perm msg_perm; /* Ownership and permissions */
3 time_t msg_stime; /* Time of last msgsnd(2) */
4 time_t msg_rtime; /* Time of last msgrcv(2) */
5 time_t msg_ctime; /* Time of last change */
6 unsigned long __msg_cbytes; /* Current number of bytes in
7 queue (non-standard) */
8 msgqnum_t msg_qnum; /* Current number of messages
9 in queue */
10 msglen_t msg_qbytes; /* Maximum number of bytes
11 allowed in queue */
12 pid_t msg_lspid; /* PID of last msgsnd(2) */
13 pid_t msg_lrpid; /* PID of last msgrcv(2) */
14 };
4 函数原型
1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/msg.h>
4 int msgget(key_t key, int msgflg);
5 说明:创建一个队列或打开一个现有队列。
6 返回:成功返回内核中消息队列的标识ID,出错返回-1。
7 参数key:用户指定的消息队列键值;
8 参数flag:IPC_CTREAT、IPC_EXCL等权限组合。
9 注:若创建消息队列,key可指定键值,也可将之设置为IPC_PRIVATE。若打开进行查询,则key不能为0,必须是一个非零的值,否则查询不到。
1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/msg.h>
4 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
5 说明:消息队列控制函数;
6 返回:成功返回0,出错返回-1;
7 参数msgid:内核中的消息队列ID;
8 参数buf:消息队列属性指针;
9 参数cmd:IPC_STAT:获取消息队列的属性,取此队列的msqid_ds结构,并将其存放在buf指向的结构中;
IPC_SET:设置属性,按由buf指向的结构中的值,设置与此队列相关的结构中的字段;
IPC_RMID:删除队列,从系统中删除该消息队列以及仍在该队列上的所有数据。
1 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
2 说明:将消息添加到消息队列尾端。
3 返回:成功返回0,出错返回-1;
4 参数msqid:内核中的消息队列ID;
5 参数msgp:通用指针,指向需要发送的消息,参数传递的格式:
6 struct msgbuf {
7 long mtype; /* message type, must be > 0 */
8 char mtext[1]; /* message data */
9 };
10 mtype:指定消息的类型,它由一个整数来代表,并且它只能是大于0的整数;
11 mtext:消息数据本身。大小由msgsz指定。
12 Linux中,消息的最大长度是4056个字节,其中包括mtype,它占4个字节;
13 结构体msgbuf用户可自定义,但第一个成员必须是mtype。
14 参数msgsz:指定消息的大小,不包括mtype的大小。
15 参数flag:0:阻塞,阻塞直到有空间可以容纳要发送的消息或从系统中删除了此队列或捕捉到一个信号,并从信号处理程序返回。
IPC_NOWAIT:类似于文件I/O的非阻塞标志。若消息队列已满(或者是队列中的消息总数等于系统限制值,或队列中的字节总数等于系统限制值),在指定IPC_NOWAIT使得msgsnd立即出错返回EAGAIN。
1 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
2 说明:从队列中取消息。
3 返回:成功返回消息的数据部分长度,出错返回-1;
4 参数msgid:消息队列ID;
5 参数msgp:指向消息队列的缓存;
6 参数msgsz:消息缓存的大小,不包括mtype的大小,计算方式:msgsz=sizeof(struct msgbuf)-sizeef(long);
7 参数msgtyp:消息类型;msgtyp==0,获得消息队列中第一个消息;msgtyp>0,获取消息队列中类型为msgtyp的第一个消息;msgtyp<0,获得消息队列中小于或等于msgtyp绝对值的消息(类型最小的)。
8 参数msgflg:0或者IPC_NOWAIT。
5 测试实例
单独创建两个进程,发送消息进程和接收消息进程,它们之间没有关系,通过消息队列来交换数据。
发送消息进程:
1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/msg.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 typedef struct {
9 long type; //消息类型
10 int start; //消息数据本身,包括start和end
11 int end;
12 }MSG;
13
14 //往消息队列中发送消息
15
16 int main(int argc, char *argv[])
17 {
18 if (argc < 2) {
19 printf("usage: %s key\n", argv[0]);
20 exit(1);
21 }
22
23 key_t key = atoi(argv[1]); //key由用户指定
24 // key_t key = ftok(argv[1], 0); //由函数生成key
25 printf("key: %d\n", key);
26
27 // 创建消息队列
28 int msg_id;
29 if ((msg_id = msgget(key, IPC_CREAT|IPC_EXCL|0777)) < 0) {
30 perror("msgget error");
31 }
32 printf("msg id: %d\n", msg_id);
33
34 // 定义要发送的消息
35 MSG m1 = {4, 4, 400};
36 MSG m2 = {2, 2, 200};
37 MSG m3 = {1, 1, 100};
38 MSG m4 = {6, 6, 600};
39 MSG m5 = {6, 40, 6000};
40
41 //发送消息到消息队列
42 if (msgsnd(msg_id, &m1, sizeof(MSG)-sizeof(long), IPC_NOWAIT) < 0) {
43 perror("msgsnd error");
44 }
45 if (msgsnd(msg_id, &m2, sizeof(MSG)-sizeof(long), IPC_NOWAIT) < 0) {
46 perror("msgsnd error");
47 }
48 if (msgsnd(msg_id, &m3, sizeof(MSG)-sizeof(long), IPC_NOWAIT) < 0) {
49 perror("msgsnd error");
50 }
51 if (msgsnd(msg_id, &m4, sizeof(MSG)-sizeof(long), IPC_NOWAIT) < 0) {
52 perror("msgsnd error");
53 }
54 if (msgsnd(msg_id, &m5, sizeof(MSG)-sizeof(long), IPC_NOWAIT) < 0) {
55 perror("msgsnd error");
56 }
57
58 // 发送后去获取消息队列中消息的总数
59 struct msqid_ds ds;
60 if (msgctl(msg_id, IPC_STAT, &ds) < 0) {
61 perror("msgctl errors");
62 }
63 printf("msgctl totol: %ld\n", ds.msg_qnum);
64
65 return 0;
66 }
接收消息进程:
1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/msg.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 typedef struct {
9 long type; //消息类型
10 int start; //消息数据本身,包括start和end
11 int end;
12 }MSG;
13
14 //往消息队列中发送消息
15 int main(int argc, char *argv[])
16 {
17 if (argc < 3) {
18 printf("usage: %s key type\n", argv[0]);
19 exit(1);
20 }
21
22 key_t key = atoi(argv[1]);
23 long type = atoi(argv[2]);
24
25 //获得指定的消息队列
26 int msg_id;
27 if ((msg_id = msgget(key, 0777)) < 0) {
28 perror("msgget error");
29 }
30 printf("msg id: %d\n", msg_id);
31
32 MSG m;
33 if (msgrcv(msg_id, &m, sizeof(MSG)-sizeof(long), type, IPC_NOWAIT) < 0) {
34 perror("msgrcv error");
35 } else {
36 printf("type: %ld start: %d end: %d\n", m.type, m.start, m.end);
37 }
38
39 return 0;
40 }
测试步骤:
1、先分别编译发送进程和接收进程
[root@192 ipc]# gcc -o bin/msg_send msg_send.c
[root@192 ipc]# gcc -o bin/msg_rcv msg_rcv.c
2、发送进程发送5条消息,key是人为指定的10
3、接收进程获取一条消息
由于消息队列中有两条type都为6的消息,可以看出,首先获得的是先发送到消息队列的消息。获取后的消息将从消息队列中删除。
4、接收进程继续获取消息
Linux间进程通信--消息队列的更多相关文章
- Linux:进程通信之消息队列Message实例
/*send.c*/ /*send.c*/ #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h&g ...
- Linux下进程通信的八种方法
Linux下进程通信的八种方法:管道(pipe),命名管道(FIFO),内存映射(mapped memeory),消息队列(message queue),共享内存(shared memory),信号量 ...
- Linux之进程通信20160720
好久没更新了,今天主要说一下Linux的进程通信,后续Linux方面的更新应该会变缓,因为最近在看Java和安卓方面的知识,后续会根据学习成果不断分享更新Java和安卓的方面的知识~ Linux进程通 ...
- Linux编程---进程通信
Linux的通信方式主要有分类有以下几种: -匿名管道和FIFO有名管道 -消息队列,信号量和共享存储 -套接字 对于套接字的进程通信,我就留在套接字的文章中再写了. 一.管道 管道是最古老的进程通信 ...
- Linux进程间通信(System V) --- 消息队列
消息队列 IPC 原理 消息队列是消息的链式队列,如下图为消息队列的模型.整个消息队列有两种类型的数据结构. 1.msqid_ds 消息队列数据结构:描述整个消息队列的属性,主要包括整个消息队列的权限 ...
- Linux 进程间通信(posix消息队列 简单)实例
Linux 进程间通信(posix消息队列 简单)实例 详情见: http://www.linuxidc.com/Linux/2011-10/44828.htm 编译: gcc -o consumer ...
- 2.Python进程间的通信之队列(Queue)和生产者消费者模型
一.队列 1.1 概念介绍-----multiprocess.Queue 创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递. Queue([maxsize] ...
- linux之间进程通信
进程间通信方式: 同主机进程间数据交换机制: pipe(无名管道) / fifo(有名管道)/ message queue(消息队列)和共享内存. 必备基础: f ...
- [置顶] 简单解析linux下进程通信方法
linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的.而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间 ...
- Linux下进程通信之管道
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把 ...
随机推荐
- Swin Transformer安装记录(国内源,飞快)
0. 设备环境 ubuntu--20.10 GPU--3080 cuda--11.0 torch--1.7.0 mmcv--1.3.8 mmdetection--2.11.0 所有的git的项目,都可 ...
- [FAQ] VisualStudio, Source file requires different compiler version (current compiler is 0.6.1+cxxxxxx)
当使用的 Solidity 库文件中 pragma 指定的 版本 与本地编译器的使用版本不一致时,会出现这类提示. 解决方式是菜单栏 View -> Extensions -> Exten ...
- WPF 性能测试
本文收藏我给 WPF 做的性能测试.在你开始认为 WPF 的性能存在问题的时候,不妨来这篇博客里找找看我做过的测试.我记录的测试都是比较纯净的测试项目,没有业务逻辑的干扰,写法也正常,可以更加真实反映 ...
- 2019-10-28-dotnet-代码调试方法
title author date CreateTime categories dotnet 代码调试方法 lindexi 2019-10-28 08:50:11 +0800 2019-6-5 9:4 ...
- petalinux 报错总结
Failed to menu config project component.... 解决办法 此处是由于Terminal(终端)的界面太窄导致的,把Terminal(终端)界面拉宽即可:重新执行命 ...
- ESP32 + IDF + LED
一.开发板 ESP32-S3-DevKitC-1 管脚布局 由于这个程序控制比较简单,就不赘述了,直接看程序. 二.程序 #include "freertos/FreeRTOS.h" ...
- 基于FPGA的音乐蜂鸣器设计
蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机.打印机.复印机.报警器.电子玩具.汽车电子设备.电话机.定时器等电子产品中作发声器件. 图1 :蜂鸣器实物图 蜂鸣器主要分为压电 ...
- 基于权电阻网络的VGA色条显示#DE10-lite#verilog#qp
- 一文搞懂drag&drop浏览器拖放功能的实现
拖放功能,即将一个元素从一个区域,通过拖拽,放置到另一个区域.常见的应用是将文件或图片从一个区域,拖放到另一个区域.中文常常把这表述成拖拽,实际上拖拽的描述并不准确,应该叫拖放,因为drag事件和dr ...
- AtCoder赛后反思
先贴上本人主页 ABC347 \(\color{blue}1624\color{red}-24\color{black}=\color{blue}1600\) 蓝名保卫战,极限 1600 C 题还是有 ...